#!/usr/bin/env python # 2011 Christoph Durr + Fadi Kacem # Speed Scaling with Power Down states # minimum Energy scheduling # single machine, agreable deadlines from sys import * from copy import * from fractions import * #constants wakeupEnergy = 10 groundEnergy = 1 alpha = 2 perfectSpeed = 1 INFINITE = 1e6 # ------------------------------------------------ instance class Job: def __init__(self,r,d,w,j): self.r = r # release time self.d = d # deadline self.w = w # workload self.j = j # index in original instance (before sorting) def __repr__(self): return "(r=%d,d=%d,w=%d,j=%d)"%(self.r, self.d, self.w, self.j) def readInstance(jobs): ''' J = all jobs, N = all job indices from 0 to n-1 ''' global J, N, n J = [] l = jobs.split() n = len(l) N = range(n) # decode jobs for j in N: try: r,d,w = map(int,l[j].split("/")) except ValueError: fatalError('Illformed job description "%s", ' \ 'try removing unecessary spaces'%l[j]) if not (0<=r and r0): fatalError('Illformed job description "%s", ' \ 'release time must be before deadline, and weight positive'%l[j]) J.append(Job(r,d,w,j)) # check if instance is agreeable J.sort(key = lambda j: (j.r, j.d)) for j in range(len(l)-1): if J[j].d > J[j+1].d: fatalError('instance does not have agreeable deadlines') # add dummy jobs, useful for computing Y[-1,n]=all jobs if not J: fatalError('Instance is empty') J.append(Job(J[n-1].d,0,0,n-1)) J.append(Job(0,J[0].r,0,-1)) # ------------------------------------------------ YDS schedule class Block: ''' a block is a sequence of jobs scheduled in a time interval at constant speed. Its total workload is the workload of its jobs ''' def __init__(self, jstart,jend, w, tstart, tend): self.jstart = jstart self.jend = jend self.w = w self.tstart = tstart self.tend = tend def len(self): return self.tend-self.tstart def __repr__(self): return "(J=%d..%d,w=%d,t=%d..%d)"% \ (self.jstart, self.jend, self.w, self.tstart, self.tend) def newPrefixSeq(i,j, jend): tend = min(J[jend].r,J[j].d) w = sum(J[k].w for k in range(i,j+1)) tstart = tend - w/perfectSpeed return block2schedule(Block(i,j,w,tstart,tend)) def newSuffixSeq(i,j,jstart): tstart = max(J[jstart].d,J[i].r) w = sum(J[k].w for k in range(i,j+1)) tend = tstart + w/perfectSpeed return block2schedule(Block(i,j,w,tstart,tend)) # A schedule is a sequence executions described by tuples # (start,end,speed,job) with speed=0,job=-1 for shutdown def block2schedule(b): if not b: return [] R = [] t = b.tstart if b.len(): s = Fraction(b.w, b.len()) else: s = 1000 for j in range(b.jstart, b.jend+1): t1 = t+J[j].w/s R.append((t,t1,s,j)) t = t1 return R def list2schedule(L): R = [] for b in L: R += block2schedule(b) return R # ------------------------------------------------ Yao, Demers, Shenker # Y[i,j] optimal YDS schedule over all jobs from i+1 to j-1 # restricted to the interval [di,rj]. Is defined only if di<=rj and if # either j=i+1 or j>i+1 and d_{i+1}>d_i and r_{j-1}=j: memY[i,j] = (0,[]) return 0 def r(q): return max(J[q].r,J[i].d) def d(q): return min(J[q].d,J[j].r) L = [(d(l)-r(k), k,l, sum(J[q].w for q in range(k,l+1))) for k in range(i+1,j) for l in range(k,j)] len,k,l,w = min(L) if len<=0: cost = INFINITE sched = [] else: len,k,l,w = max(L, key=lambda (len,k,l,w): Fraction(w,len)) s = Fraction(w,len) cost = len*(s**alpha) sched = block2schedule(Block(k,l,w,r(k),d(l))) if (k,l)!=(i+1,j-1): cost = Ycost(i,k) + cost + Ycost(l,j) sched = Ysched(i,k) + sched + Ysched(l,j) memY[i,j] = (cost,sched) return cost def YfullCost(i,j): r = Ycost(i,j) + max(0,(J[j].r-J[i].d))*groundEnergy return r def Ysched(i,j): Ycost(i,j) return memY[i,j][1] # ------------------------------------------------ prefix, suffix def compute_pre_suffixes(i,j): # prefixes for the subinstance for jobs from i+1 to j-1 included t = min(J[j].r, J[j-1].d) prefix = [0 for _ in N] jend = j-1 for k in range(j-1,i,-1): if J[k].d < t: jend = k t = J[k].d t -= Fraction(J[k].w, perfectSpeed) prefix[k] = jend # suffixes for the subinstance for jobs from i+1 to j-1 included t = max(J[i].d, J[i+1].r) suffixes = [] jstart = i+1 for k in range(i+1,j): if t= perfectSpeed: if not S: # initial subinstance S = solveInitial(job) elif jstart+1 < job: # inner subinstance S += optSched(jstart, job) # block of fast jobs S.append(b) jstart = job # is there a final subinstance ? if jstart\n' \ '\n' \ '%s' \ '\n' def schedule2svg(msg, S): # constants w = 18 d = 3 width = w * max(30,(J[n-1].d + 2)) base = w * max(2,max(speed for (_,_,speed,_) in S)) height = base+(n+3)*w+d print svgHeader % (width,height,width,height,d, height-2*d, msg) down = [] # job release time deadline span with processing time for j in N: y1 = base+(J[j].j+2)*w x1 = J[j].r*w x2 = J[j].d*w y2 = y1-d L = [(x1,y2),(x1,y1),(x2,y1),(x2,y2)] for i in range(len(L)-1): print '\n'%(L[i][0],L[i][1], L[i+1][0],L[i+1][1]) print 'w%d=%d' \ %(x1+d, y2, j+1,J[j].w) # compute each job execution for (tstart,tend,speed,job) in S: if job==-1: #shutdown interval down.append((tstart,tend)) else: # job execution if speed==1: color="orange" elif speed>1: color="red" else: color="yellow" s = int(speed*w) y = base - s x = tstart*w l = (tend-tstart)*w h = s print '' \ %(x,y,l,h,color) if speed<1: y = base - s -d else: y = y+h-d print '%d' \ %(x+d,y,job+1) # show grey lines, for activity if S: tstart = S[0][0] tend = S[-1][1] if down: activities = [(tstart, down[0][0])] activities += [(down[i-1][1],down[i][0]) for i in range(1,len(down))] activities += [(down[-1][1], tend)] else: activities = [(tstart, tend)] for x1,x2 in activities: print '' %(x1*w,base+d,(x2-x1)*w,d) print '' def fatalError(msg): print svgHeader % (1000,50,1000,50,3, 45, "Fatal error: "+msg) print '' sys.exit(1) def cost(S): tstart = S[0][0] tend = S[-1][1] L = {} L[0] = tend-tstart L[-1] = 0 for (tstart,tend,speed,job) in S: len = tend-tstart if job==-1: L[0] -= len L[-1] += 1 elif speed in L: L[speed] += len else: L[speed] = len total = wakeupEnergy * L[-1] + groundEnergy * L[0] for s in L: if s>0: total += L[s] * s**alpha res = "Energy cost = %g [%dL + %gg" \ %(total,L[-1],L[0]) for s in L: if s>0: res += "+ %g(%s)ª"%(L[s],s) return res+"]" # ------------------------------------------------ main program if (len(argv)!=2): print 'Usage: minEnergyAgreeable.py ""' exit(0) readInstance(argv[1]) S = solve() schedule2svg(cost(S), S)