Commit 74f8e6dc authored by Georgios Dagkakis's avatar Georgios Dagkakis

new version of algorithm copied

parent 8c8ef0db
......@@ -23,12 +23,13 @@ Created on 15 Dec 2014
@author: Anna
'''
from AllocationRoutine_2 import AllocationRoutine2
from AllocationRoutine_Final2 import AllocationRoutine_Final
from AllocationRoutine_Forecast import AllocationRoutine_Forecast
from Allocation_GA import Allocation_GA
from Allocation_ACO import Allocation_ACO
from Globals import G
import tablib
from copy import deepcopy
def AllocManagement_Hybrid():
......@@ -62,7 +63,7 @@ def AllocManagement_Hybrid():
def AllocManagement_Hybrid2():
def AllocManagement_Hybrid2(bestAnt):
# allocate items based on type and priority level
......@@ -70,7 +71,7 @@ def AllocManagement_Hybrid2():
ACOresults = tablib.Dataset(title='ACO_'+'order'+'_'+str(priority))
ACOresults.headers = ('Week', 'generation', 'replication', 'antID', 'excess', 'lateness', 'earliness', 'targetUtil', 'minUtil')
resAnt = {}
# starting from first week in planning horizon, complete the allocation of all orders within the same priority group
for week in G.WeekList:
......@@ -80,12 +81,25 @@ def AllocManagement_Hybrid2():
if G.ACOdefault:
G.popSize = int(0.75*len(G.sortedOrders['order'][priority][week]) - 0.75*len(G.sortedOrders['order'][priority][week])%2)
G.noGen = 20*G.popSize
ACOresults = Allocation_ACO(week, G.sortedOrders['order'][priority][week],'order',ACOresults)
ACOresults, anting = Allocation_ACO(week, G.sortedOrders['order'][priority][week],'order',ACOresults)
z=resAnt.copy()
z.update(anting)
resAnt = deepcopy(z)
else:
AllocationRoutine2(week, G.sortedOrders['order'][priority][week],'order')
if bestAnt!=None:
AllocationRoutine_Final(week, G.sortedOrders['order'][priority][week],'order',bestAnt)
else:
AllocationRoutine_Final(week, G.sortedOrders['order'][priority][week],'order',0)
if G.ACO:
G.reportResults.add_sheet(ACOresults)
G.reportResults.add_sheet(ACOresults)
return resAnt
else:
return None
def AllocManagement_Hybrid2_Forecast():
print 'start forecast allocation'
for priority in G.priorityList['forecast']:
......@@ -120,6 +134,3 @@ def AllocManagement_Hybrid2():
AllocationRoutine_Forecast(week,orderList,'forecast',{'seq':orderIDlist})
if G.GA:
G.reportResults.add_sheet(GAresults)
......@@ -27,10 +27,11 @@ from Globals import G
from pulp import *
from os import remove
from glob import glob
from time import time
def Allocation_IP(item, week, previousAss, capacity, weightFactor):
startLP = time()
MAlist = item['MAlist']
# calculate number of units to be allocated
......@@ -55,7 +56,8 @@ def Allocation_IP(item, week, previousAss, capacity, weightFactor):
# first objective: max SP units allocated to a week
BS = [float(G.BatchSize[ma][week]) for ma in MAlist]
h1=[MA_var[ma]*(weightFactor[0]/min(BS)) for ma in MAlist]
if weightFactor[0]:
h1=[MA_var[ma]*(weightFactor[0]*min(BS)/allQty) for ma in MAlist]
# print 'h1', h1, allQty
#_________________________________________________
......@@ -81,44 +83,48 @@ def Allocation_IP(item, week, previousAss, capacity, weightFactor):
utilisation[bottleneck] = 1.0/float(G.Capacity[bottleneck][week]['OriginalCapacity']) *capDict_obj[bottleneck]
Util[bottleneck] = utilisation[bottleneck]*-1
# delta target utilisation calculation (through definition of constraints)
prob += lpSum([(utilisation[bottleneck] - G.Capacity[bottleneck][week]['targetUtilisation'])/float(G.Capacity[bottleneck][week]['targetUtilisation'])]) >= Delta_targetUt[bottleneck]
prob += lpSum([(G.Capacity[bottleneck][week]['targetUtilisation'] - utilisation[bottleneck])/float(G.Capacity[bottleneck][week]['targetUtilisation'])]) >= Delta_targetUt[bottleneck]
h1.append(Util[bottleneck]*weightFactor[1] for bottleneck in G.Bottlenecks)
h1.append(Delta_targetUt[bottleneck]*weightFactor[2] for bottleneck in G.Bottlenecks)
# third set of variables (Delta_Ut represents the delta between target utilisation
Delta_Ut = LpVariable.dicts("DeltaTarget",[(b1,b2) for i1, b1 in enumerate(G.Bottlenecks) for b2 in G.Bottlenecks[i1+1:]])
if weightFactor[1]:
h1+=[Util[bottleneck]*weightFactor[1] for bottleneck in G.Bottlenecks]
if weightFactor[2]:
h1+=[Delta_targetUt[bottleneck]*weightFactor[2] for bottleneck in G.Bottlenecks]
for i1, b1 in enumerate(G.Bottlenecks):
for b2 in G.Bottlenecks[i1+1:]:
prob += lpSum([Util[b1],-1*Util[b2]]) >= Delta_Ut[(b1,b2)]
prob += lpSum([Util[b2],-1*Util[b1]]) >= Delta_Ut[(b1,b2)]
#prob += lpSum([Delta_targetUt[b1],-1*Delta_targetUt[b2]]) >= Delta_Ut[(b1,b2)]
#prob += lpSum([Delta_targetUt[b2], -1*Delta_targetUt[b1]]) >= Delta_Ut[(b1,b2)]
# aggregate objective
h1.append(Delta_Ut[(b1,b2)]*weightFactor[3] for i1, b1 in enumerate(G.Bottlenecks) for b2 in G.Bottlenecks[i1+1:])
if weightFactor[3]:
# third set of variables (Delta_Ut represents the delta between target utilisation
Delta_Ut = LpVariable.dicts("DeltaTarget",[(b1,b2) for i1, b1 in enumerate(G.Bottlenecks) for b2 in G.Bottlenecks[i1+1:]])
for i1, b1 in enumerate(G.Bottlenecks):
for b2 in G.Bottlenecks[i1+1:]:
prob += lpSum([Util[b1],-1*Util[b2]]) >= Delta_Ut[(b1,b2)]
prob += lpSum([Util[b2],-1*Util[b1]]) >= Delta_Ut[(b1,b2)]
#prob += lpSum([Delta_targetUt[b1],-1*Delta_targetUt[b2]]) >= Delta_Ut[(b1,b2)]
#prob += lpSum([Delta_targetUt[b2], -1*Delta_targetUt[b1]]) >= Delta_Ut[(b1,b2)]
h1+=[Delta_Ut[(b1,b2)]*weightFactor[3] for i1, b1 in enumerate(G.Bottlenecks) for b2 in G.Bottlenecks[i1+1:]]
#___________________________________________________________________________________
# third objective: support proportional disaggregation of SP into corresponding MAs
# create set of variables reporting the delta assignment across the MAs belonging to a SP
Delta_MA = LpVariable.dicts("SPdistribution",MAlist)
# calculate the delta assignment of MAs corresponding to SPs (through constraints)
for ma in MAlist:
if ma in item['suggestedMA']:
prob += lpSum((previousAss[ma] + MA_var[ma] * G.BatchSize[ma][week] - item['suggestedMA'][ma])/ item['Qty']) >= Delta_MA[ma]
prob += lpSum((item['suggestedMA'][ma] - previousAss[ma] - MA_var[ma] * G.BatchSize[ma][week])/ item['Qty']) >= Delta_MA[ma]
if weightFactor[4]:
# create set of variables reporting the delta assignment across the MAs belonging to a SP
Delta_MA = LpVariable.dicts("SPdistribution",MAlist)
# calculate the delta assignment of MAs corresponding to SPs (through constraints)
for ma in MAlist:
if ma in item['suggestedMA']:
prob += lpSum((previousAss[ma] + MA_var[ma] * G.BatchSize[ma][week] - item['suggestedMA'][ma])/ item['Qty']) >= Delta_MA[ma]
prob += lpSum((item['suggestedMA'][ma] - previousAss[ma] - MA_var[ma] * G.BatchSize[ma][week])/ item['Qty']) >= Delta_MA[ma]
h1+=[Delta_MA[ma]*weightFactor[4] for ma in MAlist]
#_____________________________
# aggregate and set objectives
h1.append(Delta_MA[ma]*0.5 for ma in MAlist)
# aggregate and set objectives
prob += lpSum(h1)
......@@ -141,10 +147,15 @@ def Allocation_IP(item, week, previousAss, capacity, weightFactor):
prob.writeLP("IPifx.lp")
prob.solve()
print 'lp results', item['sp'], allQty, LpStatus[prob.status], sum(MA_var[ma].varValue* G.BatchSize[ma][week] for ma in MAlist)
allocation = {}
for ma in MAlist:
allocation[ma] = MA_var[ma].varValue * G.BatchSize[ma][week]
# print ma, MA_var[ma].varValue
if LpStatus[prob.status] != 'Optimal':
print 'WARNING: LP solution ', LpStatus[prob.status]
# remove lp files
files = glob('*.mps')
......@@ -155,4 +166,5 @@ def Allocation_IP(item, week, previousAss, capacity, weightFactor):
for f in files:
remove(f)
return allocation
G.LPtime += startLP
return allocation, LpStatus[prob.status]
......@@ -25,19 +25,19 @@ Created on 28 Jan 2015
from Globals import G
from Allocation_3 import Allocation2
from UtilisationCalculation import utilisationCalc2, utilisationCalc1
from UtilisationCalculation import utilisationCalc2, utilisationCalc1, utilisationCalc3
from copy import deepcopy
# allocates orders of a give week/priority level implementing the ant choice for MAs
def AllocationRoutine_ACO(initialWeek, itemList, itemType, ant):
ACOexcess = 0
ACOcapacityDict = deepcopy(G.CurrentCapacityDict)
ACOincompleteBatches = deepcopy(G.incompleteBatches)
ACOearliness = 0
ACOlateness = 0
ACOtargetUtil = 0
ACOminUtil = 0
ACOexcess = 0
# repeat allocation procedure for all items in the list
for item in itemList:
......@@ -97,8 +97,8 @@ def AllocationRoutine_ACO(initialWeek, itemList, itemType, ant):
if chosenMA != None:
ACOcapacityDict = Results[chosenMA]['remainingCap']
ACOincompleteBatches = Results[chosenMA]['remUnits']
ACOearliness += Results[chosenMA]['earliness']/item['Qty']
ACOlateness += Results[chosenMA]['lateness']/item['Qty']
ACOearliness += float(Results[chosenMA]['earliness'])/item['Qty']
ACOlateness += float(Results[chosenMA]['lateness'])/item['Qty']
break
step += 1
......@@ -109,7 +109,7 @@ def AllocationRoutine_ACO(initialWeek, itemList, itemType, ant):
if G.minDeltaUt:
ACOtargetUtil, ACOminUtil = utilisationCalc1(ACOcapacityDict, initialWeek, ind)
else:
ACOtargetUtil, ACOminUtil = utilisationCalc2(ACOcapacityDict, initialWeek, ind)
ACOtargetUtil, ACOminUtil = utilisationCalc3(ACOcapacityDict, initialWeek, ind)
return {'ant':ant, 'excess':ACOexcess, 'earliness':ACOearliness, 'lateness':ACOlateness, 'targetUtil':ACOtargetUtil, 'minUtil':ACOminUtil}
......
# ===========================================================================
# Copyright 2015 Dublin City University
#
# This file is part of DREAM.
#
# DREAM is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DREAM is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with DREAM. If not, see <http://www.gnu.org/licenses/>.
# ===========================================================================
'''
Created on 9 Dec 2014
@author: Anna
'''
from Globals import G
from Allocation_3 import Allocation2
from copy import deepcopy
def AllocationRoutine_Final(initialWeek, itemList, itemType, ant):
excess = []
# repeat allocation procedure for all items in the list
for item in itemList:
#================================================
# Allocation step 1...allocation at current Week
#================================================
Results = {}
step = 1
ind = G.WeekList.index(initialWeek)
weekList = [initialWeek]
capacity = deepcopy(G.CurrentCapacityDict)
qty = item['Qty']
Allocation = []
earliness = 0
lateness = 0
ma = ant[item['orderID']]
chosenMA = None
while step <= 3:
if step == 2:
weekList = [G.WeekList[i] for i in range(ind-1, max(-1,ind-G.maxEarliness-1), -1)]
if step == 3:
weekList = [G.WeekList[i] for i in range(ind+1, min(G.planningHorizon,ind+G.maxLateness+1))]
if len(weekList) == 0:
step+=1
continue
if step > 1:
capacity = deepcopy(Results[ma]['remainingCap'])
qty = deepcopy(Results[ma]['remainingUnits'])
Allocation = deepcopy(Results[ma]['Allocation'])
earliness = deepcopy(Results[ma]['earliness'])
lateness = deepcopy(Results[ma]['lateness'])
else:
capacity = deepcopy(G.CurrentCapacityDict)
qty = item['Qty']
Allocation = []
earliness = 0
lateness = 0
# try allocation to ma
Results[ma] = Allocation2(ma, qty, weekList, capacity, G.incompleteBatches, earliness, lateness, Allocation, initialWeek)
if Results[ma]['remainingUnits'] == 0: # if the allocation is not successful delete the record of the allocation results
chosenMA = ma
# confirm the solution
if chosenMA != None:
G.CurrentCapacityDict = Results[chosenMA]['remainingCap']
G.incompleteBatches = Results[chosenMA]['remUnits']
G.Earliness[initialWeek][chosenMA]['qty'].append(item['Qty'])
G.Earliness[initialWeek][chosenMA]['earliness'].append(float(Results[chosenMA]['earliness'])/item['Qty'])
G.Lateness[initialWeek][chosenMA]['qty'].append(item['Qty'])
G.Lateness[initialWeek][chosenMA]['lateness'].append(float(Results[chosenMA]['lateness'])/item['Qty'])
G.orders[item['orderID']]['Allocation'] = Results[chosenMA]['Allocation']
G.orders[item['orderID']]['Excess'] = False
G.orders[item['orderID']]['chosenMA'] = chosenMA
for allRep in Results[chosenMA]['Allocation']:
G.globalMAAllocation[chosenMA][allRep['week']][itemType][item['priority']] += allRep['units']
break
step += 1
if chosenMA == None:
excess.append(item)
G.Excess[item['sp']][initialWeek] += item['Qty']
G.orders[item['orderID']]['Allocation'] = []
G.orders[item['orderID']]['Excess'] = True
G.orders[item['orderID']]['chosenMA'] = None
# for orders add allocation information
if itemType == 'order':
if chosenMA == None:
G.OrderResults.append((item['ppos'], item['sp'], item['MAlist'], item['Week'], item['Customer'], item['Qty'], item['priority'], chosenMA, 'NaN', 'NaN', 'None'))
else:
G.OrderResults.append((item['ppos'], item['sp'], item['MAlist'], item['Week'], item['Customer'], item['Qty'], item['priority'], chosenMA, Results[chosenMA]['lateness'], Results[chosenMA]['earliness'], Results[chosenMA]['Allocation']))
if itemType == 'forecast':
if chosenMA == None:
G.forecastResults.append((item['ppos'], item['sp'], item['MAlist'], item['Week'], item['Qty'], item['priority'], chosenMA, 'NaN', 'NaN', 'None'))
else:
G.forecastResults.append((item['ppos'], item['sp'], item['MAlist'], item['Week'], item['Qty'], item['priority'], chosenMA, Results[chosenMA]['lateness'], Results[chosenMA]['earliness']/item['Qty'], Results[chosenMA]['Allocation']))
......@@ -30,8 +30,6 @@ from Allocation_3 import Allocation2
def AllocationRoutine_Forecast(initialWeek, itemList, itemType, seq):
EarlinessMA = {}
LatenessMA = {}
# repeat allocation procedure for all items in the list
for order in seq['seq']:
......@@ -39,6 +37,10 @@ def AllocationRoutine_Forecast(initialWeek, itemList, itemType, seq):
item = itemList[order]
# print 'item', item['orderID']
EarlinessMA = {}
LatenessMA = {}
lateForecast = 0
earlyForecast = 0
#================================================
# Allocation step 1...allocation at current Week
#================================================
......@@ -47,7 +49,9 @@ def AllocationRoutine_Forecast(initialWeek, itemList, itemType, seq):
step = 1
ind = G.WeekList.index(initialWeek)
weekList = [initialWeek]
weekLateness = [0]
capacity = deepcopy(G.CurrentCapacityDict)
incompleteBatches = deepcopy(G.incompleteBatches)
qty = item['Qty']
Allocation = []
earliness = 0
......@@ -55,31 +59,42 @@ def AllocationRoutine_Forecast(initialWeek, itemList, itemType, seq):
previousAss = {}
for ma in G.SPlist[item['sp']]:
previousAss[ma] = 0
previousAss[ma] = incompleteBatches[ma]
# print 'ma in', ma, incompleteBatches[ma]
qty -= incompleteBatches[ma]
# make use of incomplete batch units available
incompleteBatches[ma] = 0
# print 'order qty', qty
while step <= 3 and qty>0:
if step == 2:
weekList = [G.WeekList[i] for i in range(ind-1, max(-1,ind-G.maxEarliness-1), -1)]
weekLateness = [i-ind for i in range(ind-1, max(-1,ind-G.maxEarliness-1), -1)]
assert (len(weekList)==len(weekLateness))
if step == 3:
weekList = [G.WeekList[i] for i in range(ind+1, min(G.planningHorizon,ind+G.maxLateness+1))]
weekLateness = [i-ind for i in range(ind+1, min(G.planningHorizon,ind+G.maxLateness+1))]
# print 'weeklist', weekList
if len(weekList) == 0:
step+=1
continue
# check different MAs
for week in weekList:
for weekIndex in range(len(weekList)):
week = weekList[weekIndex]
# optimise MA allocation
spAllocation = Allocation_IP(item, week, previousAss, capacity,G.weightFactor)
spAllocation, probStatus = Allocation_IP(item, week, previousAss, capacity,G.weightFactor)
# print 'all', spAllocation
# implement optimal MA solution
for ma in spAllocation.keys():
if spAllocation[ma]:
Results = Allocation2(ma, spAllocation[ma], [week], capacity, G.incompleteBatches, earliness, lateness, Allocation, initialWeek)
Results = Allocation2(ma, spAllocation[ma], [week], capacity, incompleteBatches, earliness, lateness, Allocation, initialWeek)
assert (Results['remainingUnits'] == 0)
# update variables
......@@ -91,9 +106,10 @@ def AllocationRoutine_Forecast(initialWeek, itemList, itemType, seq):
if ma not in EarlinessMA:
EarlinessMA[ma] = 0
LatenessMA[ma] = 0
EarlinessMA[ma] += max([0, initialWeek - week])*spAllocation[ma]
LatenessMA[ma] += max([0, week - initialWeek])*spAllocation[ma]
EarlinessMA[ma] += max([0, weekLateness[weekIndex]*(-1)])*spAllocation[ma]
LatenessMA[ma] += max([0, weekLateness[weekIndex]])*spAllocation[ma] #week - initialWeek
previousAss[ma] += spAllocation[ma]
if qty <= 0:
......@@ -103,14 +119,29 @@ def AllocationRoutine_Forecast(initialWeek, itemList, itemType, seq):
# confirm results
if qty <= 0:
minAss =item['Qty']*10
maIB = None
G.CurrentCapacityDict = Results['remainingCap']
G.incompleteBatches = Results['remUnits']
# print initialWeek, G.Earliness
for maT in EarlinessMA:
# assert(Results['remUnits'][maT]==0)
G.Earliness[initialWeek][maT]['qty'].append(item['Qty'])
G.Earliness[initialWeek][maT]['earliness'].append(EarlinessMA[maT]/item['Qty'])
G.Lateness[initialWeek][maT]['qty'].append(item['Qty'])
G.Lateness[initialWeek][maT]['lateness'].append(LatenessMA[maT]/item['Qty'])
lateForecast += LatenessMA[maT]/item['Qty']
earlyForecast += EarlinessMA[maT]/item['Qty']
if previousAss[maT] <= minAss:
minAss = previousAss[maT]
maIB = maT
# print 'ma ib', maIB, qty, cQty, item['Qty'], previousAss, EarlinessMA , [G.incompleteBatches[ma] for ma in G.SPlist[item['sp']]]
if maIB != None:
G.incompleteBatches[maIB] -= qty
# print [G.incompleteBatches[ma] for ma in G.SPlist[item['sp']]]
G.orders[item['orderID']]['Allocation'] = Results['Allocation']
G.orders[item['orderID']]['Excess'] = False
chosenMA = []
......@@ -119,14 +150,22 @@ def AllocationRoutine_Forecast(initialWeek, itemList, itemType, seq):
chosenMA.append(allMA['ma'])
G.orders[item['orderID']]['chosenMA'] = chosenMA
if lateForecast:
G.LateMeasures['noLateOrders'] += 1
G.LateMeasures['lateness'].append(lateForecast)
if earlyForecast:
G.LateMeasures['noEarlyOrders'] += 1
G.LateMeasures['earliness'].append(earlyForecast)
for allRep in Results['Allocation']:
G.globalMAAllocation[allRep['ma']][allRep['week']][itemType][item['priority']] += allRep['units']
G.globalMAAllocationIW[allRep['ma']][initialWeek][itemType][item['priority']] += allRep['units']
if itemType == 'forecast':
G.forecastResults.append((item['ppos'], item['sp'], item['MAlist'], item['Week'], item['Qty'], item['priority'], chosenMA, Results['lateness'], Results['earliness']/item['Qty'], Results['Allocation']))
G.forecastResults.append((item['ppos'], item['sp'], item['MAlist'], item['Week'], item['Qty'], item['priority'], chosenMA, Results['lateness']/item['Qty'], Results['earliness']/item['Qty'], Results['Allocation']))
else:
......@@ -134,5 +173,8 @@ def AllocationRoutine_Forecast(initialWeek, itemList, itemType, seq):
G.orders[item['orderID']]['Allocation'] = []
G.orders[item['orderID']]['Excess'] = True
G.orders[item['orderID']]['chosenMA'] = None
G.globalMAAllocationIW[item['sp']][initialWeek][itemType][item['priority']] += item['Qty']
G.LateMeasures['noExcess'] += 1
G.LateMeasures['exUnits'] += item['Qty']
if itemType == 'forecast':
G.forecastResults.append((item['ppos'], item['sp'], item['MAlist'], item['Week'], item['Qty'], item['priority'], None, 'NaN', 'NaN', 'None'))
......@@ -31,12 +31,10 @@ from Globals import G
from AllocationForecast_IP import Allocation_IP
from Allocation_3 import Allocation2
from UtilisationCalculation import utilisationCalc1, utilisationCalc2
from math import ceil
def AllocationRoutine_ForecastGA(initialWeek, itemList, itemType, chromo):
EarlinessMA = {}
LatenessMA = {}
GAexcess = 0
GAcapacityDict = deepcopy(G.CurrentCapacityDict)
GAincompleteBatches = deepcopy(G.incompleteBatches)
......@@ -50,7 +48,7 @@ def AllocationRoutine_ForecastGA(initialWeek, itemList, itemType, chromo):
item=itemList[order]
# print 'item', item['orderID']
print 'item', item['orderID']
#================================================
# Allocation step 1...allocation at current Week
......@@ -90,28 +88,37 @@ def AllocationRoutine_ForecastGA(initialWeek, itemList, itemType, chromo):
for week in weekList:
# optimise MA allocation at current week
spAllocation = Allocation_IP(item, week, previousAss, capacity, G.weightFactor)
spAllocation, probStatus = Allocation_IP(item, week, previousAss, capacity, G.weightFactor)
# implement optimal MA solution to update temporary variables
for ma in spAllocation.keys():
if probStatus == 'Optimal':
allocatedQty = spAllocation[ma]
else:
allocatedQty = ceil(spAllocation[ma])
if spAllocation[ma]:
Results = Allocation2(ma, spAllocation[ma], [week], capacity, inBatches, earliness, lateness, Allocation, initialWeek)
assert (Results['remainingUnits'] == 0)
#print 'spAllocation', ma, spAllocation[ma], inBatches[ma]
Results = Allocation2(ma, allocatedQty, [week], capacity, inBatches, earliness, lateness, Allocation, initialWeek)
#print 'rem units', Results['remainingUnits']
if probStatus == 'Optimal':
assert (Results['remainingUnits'] == 0)
else:
allocatedQty -= Results['remainingUnits']
# update order variables
capacity = deepcopy(Results['remainingCap'])
inBatches = deepcopy(Results['remUnits'])
qty -= spAllocation[ma]
qty -= allocatedQty
Allocation = deepcopy(Results['Allocation'])
earliness = deepcopy(Results['earliness'])
lateness = deepcopy(Results['lateness'])
if ma not in EarlinessMA:
EarlinessMA[ma] = 0
LatenessMA[ma] = 0
EarlinessMA[ma] += max([0, initialWeek - week])*spAllocation[ma]
LatenessMA[ma] += max([0, week - initialWeek])*spAllocation[ma]
previousAss[ma] += spAllocation[ma]
previousAss[ma] += allocatedQty
# if order has been fully allocated update GA variables
if qty <= 0:
......
......@@ -93,8 +93,10 @@ def Allocation2(currentMA, qty, weekList, capIn, inBatches, earliness, lateness,
currentCapacity[bottleneck][currentWeek]=remainingCapacity[bottleneck]
utilisation[bottleneck][currentWeek] = float(G.Capacity[bottleneck][currentWeek]['OriginalCapacity'] - currentCapacity[bottleneck][currentWeek])/G.Capacity[bottleneck][currentWeek]['OriginalCapacity']
Allocation.append({'ma':currentMA, 'units':correctedQty, 'week':currentWeek})
lateness += max([0, currentWeek - demandWeek])*correctedQty
earliness += max([0, demandWeek - currentWeek])*correctedQty
if currentWeek > demandWeek:
lateness += (step+1)*correctedQty
elif currentWeek<demandWeek:
earliness += (step+1)*correctedQty
# if there is a surplus...update remUnits
if surplus:
......@@ -102,7 +104,7 @@ def Allocation2(currentMA, qty, weekList, capIn, inBatches, earliness, lateness,
if roundDown:
remUnits[currentMA] -= surplus
else:
remUnits[currentMA] = G.BatchSize[currentMA][currentWeek] - surplus
remUnits[currentMA] += G.BatchSize[currentMA][currentWeek] - surplus
surplus = 0
......@@ -131,8 +133,10 @@ def Allocation2(currentMA, qty, weekList, capIn, inBatches, earliness, lateness,
currentCapacity[bottleneck][currentWeek]-=allocableQty*G.RouteDict[currentMA][bottleneck][currentWeek]
Allocation.append({'ma':currentMA,'units':allocableQty, 'week':currentWeek})
lateness += max([0, currentWeek - demandWeek])*allocableQty
earliness += max([0, demandWeek - currentWeek])*allocableQty
if currentWeek > demandWeek:
lateness += (step+1)*allocableQty
elif currentWeek < demandWeek:
earliness += (step+1)*allocableQty
if remainingUnits == 0:
sufficient = True
......
......@@ -26,7 +26,7 @@ Created on 8 Dec 2014
''' implements ACO for the allocation of orders/forecast of a certain week/priority level '''
from AllocationRoutine_ACO2 import AllocationRoutine_ACO
from AllocationRoutine_Final import AllocationRoutine_Final
from AllocationRoutine_Final2 import AllocationRoutine_Final
from Globals import G
from random import choice
from operator import itemgetter
......@@ -52,71 +52,87 @@ def ranking(candidates,elg):
fittestLateness = sorted(finalCandidates, key=itemgetter('excess', 'lateness', 'earliness'))
fittestUtilisation = sorted(finalCandidates, key=itemgetter('targetUtil', 'minUtil'))
# select candidates that appear in the first positions in both lists...with preference on Lateness metrics
numTop = int(ceil(len(finalCandidates)*0.2))
aLateness = []
aUtilisation = []
for i in range(numTop):
aLateness.append(fittestLateness[i]['orderSequence'])
aUtilisation.append(fittestUtilisation[i]['orderSequence'])
aLateness = set(aLateness)
aUtilisation = set(aUtilisation)
selection = list(aLateness.intersection(aUtilisation))
fittest = []
fitID = []
numFit = min([len(selection), elg])
for i in range(numFit):
x = 0
while x < numTop:
if fittestLateness[x]['orderSequence'] == selection[i]:
fittest.append(fittestLateness[x])
fitID.append(fittestLateness[x]['orderSequence'])
break
x += 1
# FIXME: verificare se sono tutti 0 lateness excess e earliness...in tal caso prendere direttamente da fittestUtilisation
if fittestLateness[0]['excess']==fittestLateness[len(fittestLateness)-1]['excess'] and \
fittestLateness[0]['lateness']==fittestLateness[len(fittestLateness)-1]['lateness'] and \
fittestLateness[0]['earliness']==fittestLateness[len(fittestLateness)-1]['earliness']:
# if there is not enough ants in selection then calculate the average ranking between the two sorted lists and
# select the ants with highest average ranking not previously included
# solve the ties in favour of lateness
if numFit < elg:
# select the best solutions based on fittestUtilisation
fittest = []
fitID = []
numFit = min([len(fittestUtilisation), elg])
for i in range(numFit):
fittest.append(fittestUtilisation[i])
fitID.append(fittestUtilisation[i]['orderSequence'])
else:
# select candidates that appear in the first positions in both lists...with preference on Lateness metrics
numTop = int(ceil(len(finalCandidates)*0.2))
aLateness = []
aUtilisation = []
for i in range(numTop):
aLateness.append(fittestLateness[i]['orderSequence'])
aUtilisation.append(fittestUtilisation[i]['orderSequence'])
aLateness = set(aLateness)
aUtilisation = set(aUtilisation)
selection = list(aLateness.intersection(aUtilisation))
ultRanking = {}
rankingList = []
for i in range(len(fittestLateness)):
ultRanking[fittestLateness[i]['orderSequence']] = {'lateness':i, 'orderSequence':fittestLateness[i]['orderSequence']}
for i in range(len(fittestUtilisation)):
ultRanking[fittestUtilisation[i]['orderSequence']]['utilisation'] = i
ultRanking[fittestUtilisation[i]['orderSequence']]['avg'] = mean([ultRanking[fittestUtilisation[i]['orderSequence']]['utilisation'], ultRanking[fittestUtilisation[i]['orderSequence']]['lateness']])
rankingList.append(ultRanking[fittestUtilisation[i]['orderSequence']])
fittest = []
fitID = []
numFit = min([len(selection), elg])
rankingList = sorted(rankingList, key=itemgetter('avg', 'lateness'))
for i in range(numFit):
x = 0
while x < numTop:
if fittestLateness[x]['orderSequence'] == selection[i]:
fittest.append(fittestLateness[x])
fitID.append(fittestLateness[x]['orderSequence'])
break
x += 1
for i in range(len(rankingList)):
if rankingList[i]['orderSequence'] not in fitID:
# if there is not enough ants in selection then calculate the average ranking between the two sorted lists and
# select the ants with highest average ranking not previously included
# solve the ties in favour of lateness
if numFit < elg:
ultRanking = {}
rankingList = []
for i in range(len(fittestLateness)):
ultRanking[fittestLateness[i]['orderSequence']] = {'lateness':i, 'orderSequence':fittestLateness[i]['orderSequence']}
for i in range(len(fittestUtilisation)):
ultRanking[fittestUtilisation[i]['orderSequence']]['utilisation'] = i
ultRanking[fittestUtilisation[i]['orderSequence']]['avg'] = mean([ultRanking[fittestUtilisation[i]['orderSequence']]['utilisation'], ultRanking[fittestUtilisation[i]['orderSequence']]['lateness']])
rankingList.append(ultRanking[fittestUtilisation[i]['orderSequence']])
rankingList = sorted(rankingList, key=itemgetter('avg', 'lateness'))
x = 0
while x < len(fittestLateness):
if fittestLateness[x]['orderSequence'] == rankingList[i]['orderSequence']:
fittest.append(fittestLateness[x])
fitID.append(fittestLateness[x]['orderSequence'])
for i in range(len(rankingList)):
if rankingList[i]['orderSequence'] not in fitID:
x = 0
while x < len(fittestLateness):
if fittestLateness[x]['orderSequence'] == rankingList[i]['orderSequence']:
fittest.append(fittestLateness[x])
fitID.append(fittestLateness[x]['orderSequence'])
break
x += 1
numFit += 1
if numFit == elg:
break
x += 1
numFit += 1
if numFit == elg:
break
termCriterion = 0
scores = [ant['excess'] for ant in fittest]
if max(scores) - min(scores) <= 0.001*min(scores): #Termination Criteria to check for convergence - in this case, if the current solutions are within 10% range
termCriterion += 1
scores = [ant['targetUtil'] for ant in fittest]
if max(scores) - min(scores) <= 0.001*min(scores): #Termination Criteria to check for convergence - in this case, if the current solutions are within 10% range
termCriterion += 1
# scores = [ant['excess'] for ant in fittest]
# if max(scores) - min(scores) <= 0.001*min(scores): #Termination Criteria to check for convergence - in this case, if the current solutions are within 10% range
# termCriterion += 1
#
# scores = [ant['targetUtil'] for ant in fittest]
# if max(scores) - min(scores) <= 0.001*min(scores): #Termination Criteria to check for convergence - in this case, if the current solutions are within 10% range
# termCriterion += 1
if termCriterion == 2:
print 'Termination Criterion Reached'
......@@ -180,7 +196,10 @@ def Allocation_ACO(initialWeek, itemList, itemType,ACOresults):
ACOresults.append((initialWeek, gen, rep, resultAnt['ant']['antID'],resultAnt['excess'], resultAnt['lateness'], resultAnt['earliness'], resultAnt['targetUtil'], resultAnt['minUtil'] ))
# rank ants and select best ones
ants, termCond = ranking(ants,10)
ants, termCond = ranking(ants,2)
for a in ants:
ACOresults.append((initialWeek, gen, rep, a['ant']['antID'],'survived', '', '', '', ''))
if termCond == 'Terminate':
break
......@@ -197,4 +216,4 @@ def Allocation_ACO(initialWeek, itemList, itemType,ACOresults):
AllocationRoutine_Final(initialWeek, itemList, itemType, ant['ant'])
ACOresults.append((initialWeek, gen, rep, ant['ant']['antID'], 'selected', '', '', '', ''))
return ACOresults
return ACOresults, ant['ant']
......@@ -100,18 +100,24 @@ def Allocation_GA(initialWeek, itemList, itemType,GAresults):
# cross-over: order2 cross-over is applied
for item in range(G.popSizeGA):
#print 'item', item
# apply X-over based on X-over probability
xOver = random.random()
#print 'xOver', xOver
if item < G.popSizeGA-1 and xOver <= G.probXover:
#print 'in for crossOver'
chromosomes[item]['chromo']['seq'], chromosomes[item+1]['chromo']['seq'] = order2x(chromosomes[item]['chromo']['seq'], chromosomes[item+1]['chromo']['seq'])
changeC[item] = 1 # both chromosomes have changes and they should be reassessed
changeC[item+1] = 1
mutation = random.random()
#print 'mut', mutation
# apply mutation based on mutation probability
if mutation <= G.probMutation:
#print 'in for mutation'
chromosomes[item]['chromo']['seq'] = displacement(chromosomes[item]['chromo']['seq'])
changeC[item] = 1
......
......@@ -23,6 +23,7 @@ Created on 5 Sep 2013
@author: Anna
'''
import tablib
from copy import deepcopy
class G:
Capacity = {}
......@@ -32,13 +33,16 @@ class G:
planningHorizon =0 # for future demand purposes
# demandFile = None
CurrentCapacityDict = {}
CurrentCapacityDictOrig = {}
Bottlenecks = []
SPlist = {}
SPs = []
BatchSize = {}
orders = {}
sortedOrders = {}
forecast = {}
ordersOrig = {}
sortedOrdersOrig = {}
#forecast = {}
incompleteBatches = {}
items ={}
WeekList=[]
......@@ -46,8 +50,10 @@ class G:
Earliness = {}
Lateness = {}
Excess = {}
LateMeasures = {'noLateOrders':0, 'lateness':[], 'noEarlyOrders':0, 'earliness':[], 'noExcess':0, 'exUnits':0}
Summary = {}
weightFactor = [10.0,1.0,0,2,0.5]
Utilisation={}
# ACO parameters
ACO = 1
......@@ -67,19 +73,88 @@ class G:
# utilisation calculation
minDeltaUt = 0
acoRange = []
minRange = {}
# output variables
reportResults = tablib.Databook()
OrderResults = tablib.Dataset(title='OrderSummary')
OrderResults.headers = ('PPOS', 'SP_NUMBER', 'MA_LIST', 'REQUEST_DATE', 'DFSELLER', 'ORDERQTY', 'PRIORITY', 'CHOSEN_MA', 'LATENESS', 'EARLINESS', 'ALLOCATION')
OrderResults.headers = ('OrderID', 'SP_NUMBER', 'MA_LIST', 'REQUEST_DATE', 'DFSELLER', 'ORDERQTY', 'PRIORITY', 'CHOSEN_MA','ORDERED_MA_LIST', 'LATENESS', 'EARLINESS', 'ALLOCATION')
forecastResults = tablib.Dataset(title='ForecastSummary')
forecastResults.headers = ('PPOS', 'SP_NUMBER', 'MA_LIST', 'REQUEST_DATE', 'ORDERQTY', 'PRIORITY', 'CHOSEN_MA', 'LATENESS', 'EARLINESS', 'ALLOCATION')
globalMAAllocation = {}
globalMAAllocationIW = {}
spForecastOrder = []
CapacityResults = None
CapacityResults = tablib.Dataset(title = 'BN_Capa')
allocationResults = tablib.Dataset(title = 'Demand_coverage')
Utilisation = {}
Butilisation = {}
LPtime = 0
capRep = None
# filterItem = 0
# filterWeek = 0
\ No newline at end of file
# filterWeek = 0
def initialiseVar():
G.CurrentCapacityDict = deepcopy(G.CurrentCapacityDictOrig)
G.orders = deepcopy(G.ordersOrig)
G.sortedOrders = deepcopy(G.sortedOrdersOrig)
#G.forecast = {}
# G.items ={}
# set lateness and earliness results
for week in G.WeekList:
G.Lateness[week] = {}
G.Earliness[week] = {}
for sp in G.SPlist.keys():
for ma in G.SPlist[sp]:
G.Lateness[week][ma] = {'qty':[], 'lateness':[]}
G.Earliness[week][ma] = {'qty':[], 'earliness':[]}
# set excess results
for sp in G.SPlist.keys():
G.Excess[sp] = {}
for week in G.WeekList:
G.Excess[sp][week] = 0
for ma in G.SPlist[sp]:
G.incompleteBatches[ma] = 0
# set
for sp in G.SPlist.keys():
G.globalMAAllocationIW[sp] = {}
for week in G.WeekList:
G.globalMAAllocationIW[sp][week] = {'order':{}, 'forecast':{}}
for priority in G.priorityList['order']:
G.globalMAAllocationIW[sp][week]['order'][priority] = 0
for priority in G.priorityList['forecast']:
G.globalMAAllocationIW[sp][week]['forecast'][priority] = 0
for ma in G.SPlist[sp]:
G.globalMAAllocation[ma] = {}
G.globalMAAllocationIW[ma] = {}
for week in G.WeekList:
G.globalMAAllocation[ma][week] = {'order':{}}
G.globalMAAllocationIW[ma][week] = {'order':{}}
for priority in G.priorityList['order']:
G.globalMAAllocation[ma][week]['order'][priority] = 0
G.globalMAAllocationIW[ma][week]['order'][priority] = 0
G.globalMAAllocation[ma][week]['forecast'] = {}
G.globalMAAllocationIW[ma][week]['forecast'] = {}
for priority in G.priorityList['forecast']:
G.globalMAAllocation[ma][week]['forecast'][priority] = 0
G.globalMAAllocationIW[ma][week]['forecast'][priority] = 0
G.LateMeasures = {'noLateOrders':0, 'lateness':[], 'noEarlyOrders':0, 'earliness':[], 'noExcess':0, 'exUnits':0}
# output variables
G.reportResults = tablib.Databook()
G.OrderResults = tablib.Dataset(title='OrderSummary')
G.OrderResults.headers = ('OrderID', 'SP_NUMBER', 'MA_LIST', 'REQUEST_DATE', 'DFSELLER', 'ORDERQTY', 'PRIORITY', 'CHOSEN_MA','ORDERED_MA_LIST', 'LATENESS', 'EARLINESS', 'ALLOCATION')
G.forecastResults = tablib.Dataset(title='ForecastSummary')
G.forecastResults.headers = ('PPOS', 'SP_NUMBER', 'MA_LIST', 'REQUEST_DATE', 'ORDERQTY', 'PRIORITY', 'CHOSEN_MA', 'LATENESS', 'EARLINESS', 'ALLOCATION')
G.CapacityResults = None
G.CapacityResults = tablib.Dataset(title = 'BN_Capa')
G.allocationResults = tablib.Dataset(title = 'Demand_coverage')
G.Utilisation = {}
G.Butilisation = {}
\ No newline at end of file
......@@ -135,8 +135,8 @@ def rankingElitist(candidates,elg):
# assuming a linear ranking parameter equal to 2, complete the probability list...see matlab files as reference
for i in range(numCand-2,-1,-1):
s[i] = s[i+1] + (2.0/numCand)*float(numCand-1-i)/(numCand-1)
s[i] = s[i+1] + (1.0/numCand)*(0.2+1.6*float(numCand-1-i)/(numCand-1)) #(2.0/numCand)*float(numCand-1-i)/(numCand-1)
for i in range(numCand-elg):
# randomly generate probability of selection
probSel = random.random()*float(s[0])
......
......@@ -56,7 +56,7 @@ def utilisationCalc1(ACOcapacityDict, initialWeek, ind):
minUtil.append(mean(array(minU)))
targetUtil.append(mean(absolute(targetU)))
ACOtargetUtil = mean(array(targetUtil))
ACOtargetUtil = std(array(targetUtil)) #mean(array(targetUtil)) #FIXME: potrebbe essere std(array(targetUtil))
ACOminUtil = mean(array(minUtil))*-1
return ACOtargetUtil, ACOminUtil
......@@ -83,3 +83,29 @@ def utilisationCalc2(ACOcapacityDict, initialWeek, ind):
return ACOtargetUtil, ACOminUtil
def utilisationCalc3(ACOcapacityDict, initialWeek, ind):
#==============================================
# calculate min and target utilisation metrics
#==============================================
# targetUtil = max avg utilisation
# minUtil = avgUtilisation
minUtil = []
targetUtil = []
for bottleneck in G.Bottlenecks:
weekList = [initialWeek] #+ [G.WeekList[i] for i in range(ind-1, max(-1,ind-G.maxEarliness-1), -1)]
minU = []
targetU = []
for week in weekList:
utilisation = float(G.Capacity[bottleneck][week]['OriginalCapacity']-ACOcapacityDict[bottleneck][week])/G.Capacity[bottleneck][week]['OriginalCapacity']
minU.append(utilisation)
targetU.append(float(utilisation - G.Capacity[bottleneck][week]['targetUtilisation'])/G.Capacity[bottleneck][week]['targetUtilisation'])
minUtil.append(mean(array(minU)))
targetUtil.append(mean(array(max(minU))))
ACOtargetUtil = max(targetUtil)
ACOminUtil = mean(array(minUtil)) #FIXME: considerare piu settimane in weeklist e std(targetUtil)
return ACOtargetUtil, ACOminUtil
# ===========================================================================
# Copyright 2015 Dublin City University
#
# This file is part of DREAM.
#
# DREAM is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DREAM is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with DREAM. If not, see <http://www.gnu.org/licenses/>.
# ===========================================================================
'''
Created on 27 Nov 2014
@author: Anna
'''
from AllocManagement_Hybrid import AllocManagement_Hybrid2
from ImportInput import ImportInput
from outputResults import outputResults
def main(input, algorithmAttributes):
assert input, 'no input is provided, the algorithm cannot run'
ImportInput(input, algorithmAttributes)
AllocManagement_Hybrid2()
outputResults()
# ===========================================================================
# Copyright 2015 Dublin City University
#
# This file is part of DREAM.
#
# DREAM is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DREAM is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with DREAM. If not, see <http://www.gnu.org/licenses/>.
# ===========================================================================
'''
Created on 22 Jun 2015
@author: Anna
'''
from AllocManagement_Hybrid import AllocManagement_Hybrid2, AllocManagement_Hybrid2_Forecast
from ImportInput import ImportInput
from outputResults import outputResults
from Globals import G, initialiseVar
import time
from numpy import mean, std, array
from operator import itemgetter
def main():
startTime = time.time()
ImportInput()
if G.ACO == "all":
G.acoRange = [0,1]
G.minRange = {0:[0,1],1:[0,1]}
elif G.ACO == "1":
G.acoRange = [0,1]
G.minRange = {0:[0,1],1:[G.minDeltaUt]}
else:
G.acoRange = [0]
G.minRange = {0:[G.minDeltaUt]}
for j in G.acoRange:
for i in G.minRange[j]:
initialiseVar()
G.minDeltaUt = i
G.ACO = j
print 'start ACO', G.ACO, 'minDelta', G.minDeltaUt
bestAnt = AllocManagement_Hybrid2(None)
# salvare risultati
G.Summary[(G.ACO,G.minDeltaUt)] = {'scenario':(G.ACO,G.minDeltaUt)}
for key in G.LateMeasures.keys():
if key == 'lateness' or key == 'earliness':
if len(G.LateMeasures[key]):
G.Summary[(G.ACO,G.minDeltaUt)][key] = mean(G.LateMeasures[key])
else:
G.Summary[(G.ACO,G.minDeltaUt)][key] = 0
else:
G.Summary[(G.ACO,G.minDeltaUt)][key] = G.LateMeasures[key]
utilisation = []
targetUt = []
for bottleneck in G.Bottlenecks:
for week in G.WeekList:
utilisation.append(float(G.Capacity[bottleneck][week]['OriginalCapacity']-G.CurrentCapacityDict[bottleneck][week])/G.Capacity[bottleneck][week]['OriginalCapacity'])
targetUt.append((G.Capacity[bottleneck][week]['targetUtilisation']-float(G.Capacity[bottleneck][week]['OriginalCapacity']-G.CurrentCapacityDict[bottleneck][week])/G.Capacity[bottleneck][week]['OriginalCapacity'])/G.Capacity[bottleneck][week]['targetUtilisation'])
G.Summary[(G.ACO,G.minDeltaUt)]['utilisation'] = mean(array(utilisation))
G.Summary[(G.ACO,G.minDeltaUt)]['targetM'] = mean(array(targetUt))
G.Summary[(G.ACO,G.minDeltaUt)]['targetStd'] = std(array(targetUt))
if G.ACO:
G.Summary[(G.ACO,G.minDeltaUt)]['ant'] = bestAnt
else:
G.Summary[(G.ACO,G.minDeltaUt)]['ant'] = None
# selection
listSummary = [G.Summary[item] for item in G.Summary.keys()]
print 'list summary', listSummary
listSummary = sorted(listSummary, key=itemgetter('exUnits', 'lateness', 'targetStd', 'utilisation','targetM', 'earliness'))
bestScenario = listSummary[0]['scenario']
aco = bestScenario[0]
minDelta = bestScenario[1]
G.Summary['orderedScenario'] = {}
for i in range(len(listSummary)):
G.Summary['orderedScenario'][listSummary[i]['scenario']] = i+1
G.Summary['bestScenario'] = bestScenario
if aco != G.ACO or minDelta != G.minDeltaUt:
initialiseVar()
G.ACO = 0 # forces the simulation of the best ant (even though the best scenario is ACO
G.minDeltaUt = minDelta
AllocManagement_Hybrid2(G.Summary[(aco,minDelta)]['ant'])
AllocManagement_Hybrid2_Forecast()
outputResults()
print 'calculation time', time.time()-startTime
if __name__ == '__main__':
main()
\ No newline at end of file
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