Module optseq2
[hide private]
[frames] | no frames]

Source Code for Module optseq2

  1  # Python Interface for the Resource Constrained Scheduling Solver OptSeq 
  2  # file name  optseq2.py 
  3  # ver. 2.0 Copyright Log Opt Co., Ltd. 
  4  import copy 
  5   
6 -class Model(object):
7 - def __init__(self):
8 """ 9 OptSeq model class. 10 - Attbibutes: 11 - activities: Dictionary that maps activity names to activity objects in the model. 12 - modes: Dictionary that maps mode names to mode objects in the model. 13 - resources: Dictionary that maps resource names to resource objects in the model. 14 - temporals: Dictionary that maps pairs of activity names to temporal constraint objects in the model. 15 - Params: Object including all the parameters of the model. 16 17 - act: List of all the activity objects in the model. 18 - res: List of all the resource objects in the model. 19 - tempo: List of all the tamporal constraint objects in the model. 20 """ 21 self.activities={} #set of activities maintained by a dictionary 22 self.modes={} #set of modes maintained by a dictionary 23 self.resources={} #set of resources maintained by a dictionary 24 self.temporals={} #set of temporal constraints maintained by a dictionary 25 26 self.act=[] #list of activity objects 27 self.res=[] #list of resource objects 28 self.tempo=[] #list of temporal constraint's objects 29 30 self.Params=Parameters() #controal parameters' class
31
32 - def __str__(self):
33 ret="number of activities= "+str(len(self.act))+"\n" 34 ret+="number of resources= "+str(len(self.res)) +"\n" 35 if len(self.act): 36 ret+="Activity Information \n" 37 for i in self.act: 38 ret+=str(i)+"\n" 39 40 if len(self.res): 41 ret+="Resource Information \n" 42 for i in self.res: 43 ret+=str(i)+"\n" 44 if len(i.terms)>0: 45 ret+=i.printConstraint() +"\n" 46 47 if len(self.tempo): 48 ret+="Temporal Constraint Information \n" 49 for t in self.tempo: 50 ret+=str(t) 51 52 return ret
53 54
55 - def addActivity(self, name="",duedate="inf",weight=1):
56 """ 57 Add an activity to the model. 58 59 - Arguments: 60 - name: Name for new activity. A string object except "source" and "sink." Remark that strings in OptSeq are restricted to a-z, A-Z, 0-9,[],_ and @. 61 - duedate(optional): Duedate of activity. A non-nagative integer or string "inf." 62 - weight(optional): Panalty of one unit of tardiness. Positive integer. 63 64 - Return value: New activity object. 65 66 - Example usage: 67 68 >>> a = model.addActivity("act1") 69 70 >>> a = model.addActivity(name="act1",duedate=20,weight=100) 71 72 >>> a = model.addActivity("act1",20,100) 73 """ 74 75 activity=Activity(name,duedate,weight) 76 self.act.append(activity) 77 #self.activities[activity.name]=activity 78 return activity
79
80 - def addResource(self,name="",capacity={},rhs=0,direction="<="):
81 """ 82 Add a resource to the model. 83 84 - Arguments: 85 - name: Name for new resource. Remark that strings in OptSeq are restricted to a-z, A-Z, 0-9,[],_ and @. 86 - capacity (optional): Capacity dictionary of the renewable (standard) resource. 87 - Capacity dictionary maps intervals (pairs of start time and finish time) to amounts of capacity. 88 - rhs (optional): Right-hand-side constant of nonrenewable resource constraint. 89 - direction (optional): Rirection (or sense) of nonrenewable resource constraint; "<=" (default) or ">=" or "=". 90 91 - Return value: New resource object. 92 93 - Example usage: 94 95 >>> r=model.addResource("res1") 96 97 >>> r=model.addResource("res1", {(0,10):1,(12,100):2} ) 98 99 >>> r=model.addResource("res2",rhs=10,direction=">=") 100 101 """ 102 res=Resource(name,capacity,rhs,direction) 103 self.res.append(res) 104 #self.resources[res.name]=res 105 return res
106
107 - def addTemporal(self,pred,succ,tempType="CS",delay=0):
108 """ 109 Add a temporal constraint to the model. 110 111 A temporal constraint has the following form:: 112 113 predecessor's completion (start) time +delay <= 114 successor's start (completion) time. 115 116 Parameter "delay" can be negative. 117 118 - Arguments: 119 - pred: Predecessor (an activity object) or string "source." 120 Here, "source" specifies a dummy activity that precedes all other activities and starts at time 0. 121 - succ: Successor (an activity object) or string "source." 122 Here, "source" specifies a dummy activity that precedes all other activities and starts at time 0. 123 - tempType (optional): String that differentiates the temporal type. 124 "CS" (default)=Completion-Start, "SS"=Start-Start, 125 "SC"= Start-Completion, "CC"=Completion-Completion. 126 - delay (optional): Time lag between the completion (start) times of two activities. 127 128 - Return value: New temporal object. 129 130 - Example usage: 131 132 >>> t=model.addTemporal(act1,act2) 133 134 >>> t=model.addTemporal(act1,act2,type="SS",delay=-10) 135 136 To specify the start time of activity act is exactly 50, we use two temporal constraints: 137 138 >>> t=model.addTemporal("source",act,type="SS",delay=50) 139 140 >>> t=model.addTemporal(act,"source",type="SS",delay=50) 141 """ 142 t=Temporal(pred,succ,tempType,delay) 143 self.tempo.append(t) 144 #self.temporals[pred.name,succ.name]=None 145 return t
146
147 - def optimize(self):
148 """ 149 Optimize the model using optseq.exe in the same directory. 150 151 - Example usage: 152 153 >>> model.optimize() 154 """ 155 LOG=self.Params.OutputFlag 156 makespan=self.Params.Makespan 157 # prepare input string 158 f="" 159 self.resources={} #dictionary of resources that maps res-name to res-object 160 for r in self.res: 161 self.resources[r.name]=r 162 f+=str(r) 163 164 self.modes={} #dictionary of modes that maps mode-name to mode-object 165 for a in self.act: 166 if len(a.modes)>=2: 167 for m in a.modes: 168 self.modes[m.name]=m 169 170 for m in self.modes: #print mode information 171 f+="mode "+str(m)+" " 172 f+=str(self.modes[m]) 173 174 self.activities={} #dictionary of activities that maps activity-name to activity-object 175 for a in self.act: 176 self.activities[a.name]=a 177 f+=str(a) 178 f+="\n" 179 180 self.temporals={} #dictionary of temporal constraints that maps activity name pair to temporal-object 181 for t in self.tempo: 182 if t.pred=="source": 183 self.temporals[("source",t.succ.name)]=t 184 elif t.succ=="source": 185 self.temporals[(t.pred.name,"succ")]=t 186 else: 187 self.temporals[(t.pred.name,t.succ.name)]=t 188 f+=str(t) 189 190 #non-renewable constraint 191 for r in self.res: 192 self.resources[r.name]=r 193 if len(r.terms)>0: 194 f+=r.printConstraint() 195 196 197 f+="\n" 198 if makespan: 199 f+="activity sink duedate 0 \n" 200 if LOG: 201 print "output: \n"+f 202 203 import subprocess 204 cmd = "OptSeq -time "+str(self.Params.TimeLimit)+ \ 205 " -backtrack "+str(self.Params.Backtruck) +\ 206 " -iteration "+str(self.Params.MaxIteration)+\ 207 " -report "+str(self.Params.ReportInterval)+\ 208 " -seed "+str(self.Params.RandomSeed)+\ 209 " -tenure "+str(self.Params.Tenure) 210 #print "cmd=",cmd 211 try: 212 pipe = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stdin=subprocess.PIPE) 213 except OSError: 214 print "error: could not execute command '%s'" % cmd 215 print "please check that the solver is in the path" 216 exit(0) 217 out, err = pipe.communicate(f) #get the result 218 219 if LOG: 220 print out, '\n' 221 222 #search strings 223 s0="--- best solution ---" 224 s1="--- tardy activity ---" 225 s2="--- resource residuals ---" 226 s3="objective value =" 227 pos0=out.find(s0)+len(s0) #job data start position 228 pos1=out.find(s1,pos0) #data end position 229 pos2=out.find(s2,pos1) 230 pos3=out.find(s3,pos2) 231 #print "data positions",pos0,pos1,pos2,pos3 232 data=out[pos0:pos1] 233 resdata=out[pos2+len(s2):pos3] 234 data=data.splitlines() 235 reslines=resdata.splitlines() 236 for line in reslines: 237 if len(line)<=1: 238 continue 239 current=line.split() 240 resname=current[0][:-1] 241 residual=current[1:] 242 count=0 243 resDic={} #residual capacity 244 while count<len(residual): 245 interval=residual[count].split(",") 246 int1=int(interval[0][1:]) 247 int2=int(interval[1][:-1]) 248 count+=1 249 num=int(residual[count]) 250 count+=1 251 resDic[(int1,int2)]=num 252 #print resname,residual 253 self.resources[resname].residual=resDic 254 255 #job data conversion 256 execute=[] 257 for i in range(len(data)): 258 replaced=data[i].replace(","," ") 259 current=replaced.split() #split by space 260 #print current 261 if len(current)>1: 262 execute.append(current) 263 for line in execute: 264 #print "line=",line 265 actname=line[0] 266 mode=line[1] 267 start=line[2] 268 execute=line[3:-1] #list for breakable activity 269 completion=line[-1] 270 print actname,mode,start,completion 271 #print "execute=",execute 272 if actname=="source": 273 pass 274 elif actname=="sink": 275 pass 276 else: 277 self.activities[actname].start=int(start) 278 self.activities[actname].completion=int(completion) 279 if mode !="---": 280 self.activities[actname].selected=mode 281 exeDic={} 282 for exe in execute: 283 exedata=exe.split("--") 284 start=exedata[0] 285 completion=exedata[1] 286 idx=completion.find("[") 287 #for parallel execution 288 if idx>0: 289 parallel=completion[idx+1:-1] 290 completion=completion[:idx] 291 #print completion,idx,parallel 292 else: 293 parallel=1 294 exeDic[(int(start),int(completion))]=parallel 295 self.activities[actname].execute=exeDic 296 return
297
298 - def write(self,filename="optseq.txt"):
299 """ 300 Output the gantt's chart as a text file. 301 302 - Argument: 303 - filename: Output file name. Default="optseq.txt." 304 305 - Example usage: 306 307 >>> model.write("sample.txt") 308 309 """ 310 f=open(filename,"w") 311 312 horizon=0 313 actList=[] 314 for a in self.activities: 315 actList.append(a) 316 act=self.activities[a] 317 horizon=max(act.completion,horizon) 318 print "planning horizon=",horizon 319 actList.sort() 320 title=" activity mode".center(20)+" duration |" 321 322 width=len(str(horizon)) #period width =largest index of time 323 for t in range(horizon): 324 num=str(t+1) 325 title+=num.rjust(width)+"|" 326 #print title 327 f.write(title+"\n") 328 f.write("-"*(30+(width+1)*horizon)+"|\n") 329 for a in actList: #sorted order 330 act=self.activities[a] #act: activity object 331 actstring=act.name.center(10)[:10] 332 if len(act.modes)>=2: 333 actstring+= str(act.selected).center(10) 334 actstring+=str(self.modes[act.selected].duration).center(10) 335 #print " executed on resource:" 336 #print self.modes[act.selected].requirement,m1.modes[act.selected].rtype 337 else: 338 #print " executed on resource:" 339 #print act.modes[0].requirement,act.modes[0].rtype 340 actstring+= str(act.modes[0].name).center(10)[:10] 341 actstring+=str(act.modes[0].duration).center(10) 342 execute=[0 for t in range(horizon)] 343 for (s,c) in act.execute: 344 para=act.execute[s,c] 345 for t in range(s,c): 346 execute[t]=int(para) 347 348 for t in range(horizon): 349 if execute[t]>=2: 350 #for res_name in self.modes[act.selected].requirement: 351 #print res_name 352 #print m1.modes[act.selected].rtype 353 #print self.modes[act.selected] 354 actstring+="|*"+str(execute[t]).rjust(width-1) 355 elif execute[t]==1: 356 actstring+="|"+"="*(width) 357 elif t>=act.start and t<act.completion: 358 actstring+="|"+"."*(width) 359 else: 360 actstring+="|"+" "*width 361 actstring+="|" 362 #print actstring 363 f.write(actstring+"\n") 364 ## ## print act.name +" starts at "+str(act.start)+" and finish at " +str(act.completion) 365 ## ## print " and is executed :"+str(act.execute)] 366 367 f.write("-"*(30+(width+1)*horizon)+"\n") 368 f.write("resource usage/capacity".center(30)+"| \n") 369 f.write("-"*(30+(width+1)*horizon)+"\n") 370 resList=[] 371 for r in self.resources: 372 resList.append(r) 373 resList.sort() 374 for r in resList: 375 res=self.resources[r] 376 if len(res.terms)==0: #output residual and capacity 377 rstring=res.name.center(30) 378 cap=[0 for t in range(horizon)] 379 residual=[0 for t in range(horizon)] 380 for (s,c) in res.residual: 381 amount=res.residual[(s,c)] 382 if c=="inf": 383 c=horizon 384 s=min(s,horizon) 385 c=min(c,horizon) 386 for t in range(s,c): 387 residual[t]+=amount 388 389 for (s,c) in res.capacity: 390 amount=res.capacity[(s,c)] 391 if c=="inf": 392 c=horizon 393 s=min(s,horizon) 394 c=min(c,horizon) 395 for t in range(s,c): 396 cap[t]+=amount 397 398 for t in range(horizon): 399 num=str(cap[t]-residual[t]) 400 rstring+="|"+num.rjust(width) 401 f.write(rstring+"|\n") 402 403 rstring= str(" ").center(30) 404 405 for t in range(horizon): 406 num=str(cap[t]) 407 rstring+="|"+num.rjust(width) 408 f.write(rstring+"|\n") 409 f.write("-"*(30+(width+1)*horizon)+"\n") 410 f.close()
411 412
413 -class Parameters():
414 """ 415 OptSeq parameter class to control the operation of SCOP. 416 417 @param TimeLimit: Limits the total time expended (in seconds). Positive integer. Default=600. 418 @param OutputFlag: Controls the output log. Boolean. Default=False (0). 419 @param RandomSeed: Sets the random seed number. Integer. Default=1. 420 @param ReportInterval: Controls the frequency at which log lines are printed (iteration number). Default=1073741823. 421 @param Backtruck: Controls the maximum backtrucks. Default=100. 422 @param MaxIteration: Sets the maximum numbers of iterations. Default=1073741823. 423 @param Tenure: Controls a parameter of tabu search (initial tabu tenure). Default=0. 424 @param Makespan: Sets the objective function. 425 Makespan is True if the objective is to minimize the makespan (maximum completion time), 426 is False otherwise, i.e., to minimize the total weighted tardiness of activities. 427 Default=False. 428 """
429 - def __init__(self):
430 self.TimeLimit=600 431 self.OutputFlag=0 #off 432 self.RandomSeed=1 433 self.ReportInterval=1073741823 434 self.Backtruck=100 435 self.MaxIteration=1073741823 436 self.Tenure=0 437 self.Makespan=False
438
439 -class Mode():
440 - def __init__(self,name="",duration=0):
441 """ 442 OptSeq mode class. 443 444 - Arguments: 445 - name: Name of mode (sub-activity). 446 Remark that strings in OptSeq are restricted to a-z, A-Z, 0-9,[],_ and @. 447 Also you cannot use "dummy" for the name of a mode. 448 - duration(optional): Processing time of mode. Default=0. 449 450 - Attbibutes: 451 - requirement: Dictionary that maps a pair of resource name and resource type (rtype) to requirement dictionary. 452 Requirement dictionary maps intervals (pairs of start time and finish time) to amounts of requirement. 453 Resource type (rtype) is None (standard resource type), "break" or "max." 454 - breakable: Dictionary that maps breakable intervals to maximum brek times. 455 - paralel: Dictionary that maps parallelable intervals to maximum parallel numbers. 456 """ 457 if name=="dummy": 458 print "source and sink cannnot be used as an activity name" 459 raise NameError 460 self.name=name 461 self.duration=duration 462 self.requirement={} 463 self.breakable={} 464 self.parallel={}
465
466 - def __str__(self):
467 ret= "duration "+str(self.duration)+"\n" 468 #resource 469 470 if self.requirement !={}: 471 for (r,rtype) in self.requirement: 472 ret+=" "+str(r)+" " 473 for (interval,cap) in self.requirement[(r,rtype)].iteritems(): 474 (s,t)=interval 475 if rtype=="max": 476 ret+="max interval "+str(s)+" "+str(t)+" requirement "+str(cap) +"\n" 477 elif rtype=="break": 478 ret+="interval break "+str(s)+" "+str(t)+" requirement "+str(cap) +"\n" 479 elif rtype==None: 480 ret+="interval "+str(s)+" "+str(t)+" requirement "+str(cap) +"\n" 481 else: 482 print "resource type error" 483 raise TypeError 484 #break 485 if self.breakable !={}: 486 for (interval,cap) in self.breakable.iteritems(): 487 (s,t)=interval 488 if cap=="inf": 489 ret+=" break interval "+str(s)+" "+str(t)+"\n" 490 else: 491 ret+=" break interval "+str(s)+" "+str(t)+" max "+str(cap) +"\n" 492 #parallel 493 if self.parallel !={}: 494 for (interval,cap) in self.parallel.iteritems(): 495 (s,t)=interval 496 if cap=="inf": 497 ret+=" parallel interval "+str(s)+" "+str(t) +"\n" 498 else: 499 ret+=" parallel interval "+str(s)+" "+str(t)+" max "+str(cap) +"\n" 500 return ret
501
502 - def addResource(self,resource,requirement={},rtype=None):
503 """ 504 Adds a resource to the mode. 505 506 - Arguments: 507 - resurce: Resource object to be added to the mode. 508 - requirement: Dictionary that maps intervals (pairs of start time and finish time) to amounts of requirement. 509 - rtype (optional): Type of resource to be added to the mode. 510 None (standard resource type; default), "break" or "max." 511 512 - Example usage: 513 514 >>> mode.addResource(worker,{(0,10):1}) 515 516 defines worker resource that uses 1 unit for 10 periods. 517 518 >>> mode.addResource(machine,{(0,"inf"):1},"break") 519 520 defines machine resource that uses 1 unit during break periods. 521 522 >>> mode.addResource(machine,{(0,"inf"):1},"max") 523 524 defines machine resource that uses 1 unit during parallel execution. 525 """ 526 527 if type(resource.name)!=type("") or type(requirement)!=type({}): 528 print "type error in adding a resource %s to activity"%(resource.name,self.name) 529 raise TypeError 530 elif rtype ==None or rtype=="break" or rtype =="max": 531 if self.requirement.has_key((resource.name,rtype)): 532 pass 533 else: 534 self.requirement[(resource.name,rtype)]={} #generate an empty dic. 535 data=copy.deepcopy(self.requirement[(resource.name,rtype)]) 536 data.update( requirement ) 537 self.requirement[(resource.name,rtype)]=data 538 else: 539 print "rtype must be None or break or max" 540 raise NameError
541
542 - def addBreak(self,start=0,finish=0,maxtime="inf"):
543 """ 544 Sets breakable information to the mode. 545 546 - Arguments: 547 - start(optional): Earliest break time. Non-negative integer. Default=0. 548 - finish(optional): Latest break time. Non-negative integer or "inf." Default=0. 549 Interval (start,finish) defines a possible break interval. 550 - maxtime(optional): Maximum break time. Non-negative integer or "inf." Default="inf." 551 552 - Example usage: 553 554 >>> mode.addBreak(0,10,1} 555 556 defines a break between (0,10) for one period. 557 """ 558 559 data=copy.deepcopy(self.breakable) 560 data.update({ (start,finish):maxtime } ) 561 self.breakable=data
562
563 - def addParallel(self,start=1,finish=1,maxparallel="inf"):
564 """ 565 Sets parallel information to the mode. 566 567 - Arguments: 568 - start(optional): Smallest job index executable in parallel. Positive integer. Default=1. 569 - finish(optional): Largest job index executable in parallel. Positive integer or "inf." Default=1. 570 - maxparallel(optional): Maximum job numbers executable in parallel. Non-negative integer or "inf." Default="inf." 571 572 - Example usage: 573 574 >>> mode.addParallel(1,1,2} 575 """ 576 data=copy.deepcopy(self.parallel) 577 data.update({ (start,finish):maxparallel } ) 578 self.parallel=data
579
580 -class Activity():
581 - def __init__(self,name="",duedate="inf",weight=1):
582 """ 583 OptSeq activity class. 584 585 You can create an activity object by adding an activity to a model (using Model.addActivity) 586 instead of by using an Activity constructor. 587 588 - Arguments: 589 - name: Name of activity. Remark that strings in OptSeq are restricted to a-z, A-Z, 0-9,[],_ and @. 590 Also you cannot use "source" and "sink" for the name of an activity. 591 - duedate(optional): Duedate of activity. A non-nagative integer or string "inf." 592 - weight(optional): Panalty of one unit of tardiness. Positive integer. 593 """ 594 595 if name=="source" or name=="sink": 596 print "source and sink cannnot be used as an activity name" 597 raise NameError 598 self.name=name 599 self.duedate=duedate 600 self.weight=weight 601 self.modes=[] #list of mode objects
602
603 - def __str__(self):
604 ret="activity "+str(self.name)+ "\n" 605 if self.duedate !="inf": 606 ret+=" duedate "+str(self.duedate) +"\n" 607 ret+=" weight "+ str(self.weight)+ "\n" 608 if len(self.modes)==1: #single mode 609 ret+="mode " 610 ret+=str(self.modes[0]) #print mode information 611 elif len(self.modes)>=2: 612 for m in self.modes: #multiple modes 613 ret+=str(m.name) +" " #print mode names 614 ret+="\n" 615 else: 616 ret+="no mode \n" 617 return ret
618
619 - def addModes(self,*modes):
620 """ 621 Adds a mode or modes to the activity. 622 623 - Arguments: 624 - modes: One or more mode objects. 625 626 - Example usage: 627 628 >>> activity.addModes(mode1,mode2) 629 """ 630 for mode in modes: 631 self.modes.append(mode)
632
633 -class Resource():
634 - def __init__(self,name="",capacity=0,rhs=0,direction="<="):
635 """ 636 OptSeq resource class. 637 638 - Arguments: 639 - name: Name of resource. 640 Remark that strings in OptSeq are restricted to a-z, A-Z, 0-9,[],_ and @. 641 - capacity (optional): Capacity dictionary of the renewable (standard) resource. 642 Capacity dictionary maps intervals (pairs of start time and finish time) to amounts of capacity. 643 - rhs (optional): Right-hand-side constant of nonrenewable resource constraint. 644 - direction (optional): Rirection (or sense) of nonrenewable resource constraint; "<=" (default) or ">=" or "=". 645 646 - Attbibutes: 647 - capacity: Capacity dictionary of the renewable (standard) resource. 648 - rhs: Right-hand-side constant of nonrenewable resource constraint. 649 - direction: Rirection (or sense) of nonrenewable resource constraint; "<=" (default) or ">=" or "=". 650 - terms: List of terms in left-hand-side of nonrenewable resource. 651 Each term is a tuple of coeffcient,activity and mode. 652 653 """ 654 655 if name==None or name=="": 656 print "error: please specify the name when creating a constraint" 657 raise NameError 658 659 self.name=name 660 self.capacity=capacity 661 self.rhs = rhs 662 self.direction = direction 663 self.terms = []
664
665 - def __str__(self):
666 ret="" 667 if self.capacity!={}: 668 ret+="resource "+str(self.name)+" \n" 669 capList=[] 670 for (interval,cap) in self.capacity.iteritems(): 671 (s,t)=interval 672 capList.append((s,t,cap)) 673 capList.sort() 674 for (s,t,cap) in capList: 675 ret+=" interval "+str(s)+" "+str(t)+" capacity "+str(cap) +"\n" 676 677 return ret
678
679 - def addCapacity(self,start=0,finish=0,amount=1):
680 """ 681 Adds a capacity to the resource. 682 683 - Arguments: 684 - start(optional): Start time. Non-negative integer. Default=0. 685 - finish(optional): Finish time. Non-negative integer. Default=0. 686 Interval (start,finish) defines the interval during which the capacity is added. 687 - amount(optional): The amount to be added to the capacity. Positive integer. Default=1. 688 689 - Example usage: 690 691 >>> manpower.addCapacity(0,5,2) 692 """ 693 694 data=copy.deepcopy(self.capacity) 695 data.update({ (start,finish):amount } ) 696 self.capacity=data
697
698 - def printConstraint(self):
699 """ 700 Returns the information of the linear constraint. 701 702 The constraint is expanded and is shown in a readable format. 703 """ 704 f = "nonrenewable " 705 for (coeff,var,value) in self.terms: 706 f += str(coeff)+"("+str(var.name) +","+ str(value.name)+") " 707 f += self.direction+str(self.rhs) 708 return f
709
710 - def addTerms(self,coeffs=[],vars=[],values=[]):
711 """ 712 Add new terms into left-hand-side of nonrenewable resource constraint. 713 714 - Arguments: 715 - coeffs: Coefficients for new terms; either a list of coefficients or a single coefficient. 716 The three arguments must have the same size. 717 - vars: Activity objects for new terms; either a list of activity objects or a single activity object. 718 The three arguments must have the same size. 719 - values: Mode objects for new terms; either a list of mode objects or a single mode object. 720 The three arguments must have the same size. 721 722 - Example usage: 723 724 >>> budget.addTerms(1,act,express) 725 726 adds one unit of nonrenewable resource (budget) if activity "act" is executed in mode "express." 727 728 """ 729 730 if type(coeffs) !=type([]): #need a check whether coeffs is numeric ... 731 self.terms.append( (coeffs,vars,values)) 732 elif type(coeffs)!=type([]) or type(vars)!=type([]) or type(values)!=type([]): 733 print "coeffs, vars, values must be lists" 734 raise TypeError 735 elif len(coeffs)!=len(vars) or len(coeffs)!=len(values) or len(values)!=len(vars): 736 print "length of coeffs, vars, values must be identical" 737 raise TypeError 738 else: 739 for i in range(len(coeffs)): 740 self.terms.append( (coeffs[i],vars[i],values[i]))
741
742 - def setRhs(self,rhs=0):
743 """ 744 Sets the right-hand-side of linear constraint. 745 746 - Argument: 747 - rhs: Right-hand-side of linear constraint. 748 749 - Example usage: 750 751 >>> L.setRhs(10) 752 753 """ 754 self.rhs = rhs
755 756
757 -class Temporal():
758 - def __init__(self,pred,succ,tempType,delay):
759 """ 760 OptSeq temporal class. 761 762 A temporal constraint has the following form:: 763 764 predecessor's completion (start) time +delay <= 765 successor's start (completion) time. 766 767 Parameter "delay" can be negative. 768 769 - Arguments: 770 - pred: Predecessor (an activity object) or string "source." 771 Here, "source" specifies a dummy activity that precedes all other activities and starts at time 0. 772 - succ: Successor (an activity object) or string "source." 773 Here, "source" specifies a dummy activity that precedes all other activities and starts at time 0. 774 - tempType (optional): String that differentiates the temporal type. 775 "CS" (default)=Completion-Start, "SS"=Start-Start, 776 "SC"= Start-Completion, "CC"=Completion-Completion. 777 - delay (optional): Time lag between the completion (start) times of two activities. 778 779 - Attributes: 780 - pred: Predecessor (an activity object) or string "source." 781 - succ: Successor (an activity object) or string "source." 782 - type: String that differentiates the temporal type. 783 "CS" (default)=Completion-Start, "SS"=Start-Start, 784 "SC"= Start-Completion, "CC"=Completion-Completion. 785 - delay: Time lag between the completion (start) times of two activities. 786 787 """ 788 789 self.pred=pred 790 self.succ=succ 791 self.type=tempType 792 self.delay=delay
793 794
795 - def __str__(self):
796 if self.pred=="source": 797 ret="temporal " +"source"+" "+str(self.succ.name) 798 elif self.succ=="source": 799 ret="temporal " +str(self.pred.name)+" "+"source" 800 else: 801 ret="temporal " +str(self.pred.name)+" "+str(self.succ.name) 802 803 ret+=" type "+str(self.type)+" delay "+ str(self.delay)+"\n" 804 return ret
805