1   
  2   
  3   
  4  import copy 
  5   
  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={}   
 22          self.modes={}        
 23          self.resources={}    
 24          self.temporals={}    
 25           
 26          self.act=[]          
 27          self.res=[]          
 28          self.tempo=[]        
 29   
 30          self.Params=Parameters()  
  31   
 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           
 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           
 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           
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           
145          return t         
 146              
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         
158          f="" 
159          self.resources={}  
160          for r in self.res: 
161              self.resources[r.name]=r 
162              f+=str(r) 
163               
164          self.modes={}        
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:   
171              f+="mode "+str(m)+" " 
172              f+=str(self.modes[m]) 
173   
174          self.activities={}   
175          for a in self.act: 
176              self.activities[a.name]=a 
177              f+=str(a) 
178          f+="\n" 
179   
180          self.temporals={}  
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           
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           
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)                   
218   
219          if LOG: 
220              print out, '\n' 
221   
222           
223          s0="--- best solution ---" 
224          s1="--- tardy activity ---" 
225          s2="--- resource residuals ---" 
226          s3="objective value =" 
227          pos0=out.find(s0)+len(s0)  
228          pos1=out.find(s1,pos0)     
229          pos2=out.find(s2,pos1) 
230          pos3=out.find(s3,pos2) 
231           
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={}  
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               
253              self.resources[resname].residual=resDic 
254   
255           
256          execute=[] 
257          for i in range(len(data)): 
258              replaced=data[i].replace(","," ") 
259              current=replaced.split()  
260               
261              if len(current)>1: 
262                  execute.append(current) 
263          for line in execute: 
264               
265              actname=line[0] 
266              mode=line[1] 
267              start=line[2] 
268              execute=line[3:-1]  
269              completion=line[-1] 
270              print actname,mode,start,completion 
271               
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                       
288                      if idx>0: 
289                          parallel=completion[idx+1:-1] 
290                          completion=completion[:idx] 
291                           
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))  
323          for t in range(horizon): 
324              num=str(t+1) 
325              title+=num.rjust(width)+"|"  
326           
327          f.write(title+"\n") 
328          f.write("-"*(30+(width+1)*horizon)+"|\n") 
329          for a in actList:  
330              act=self.activities[a]  
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                   
336                   
337              else: 
338                   
339                   
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                       
351                           
352                       
353                       
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               
363              f.write(actstring+"\n") 
364   
365   
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:  
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() 
 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      """ 
430          self.TimeLimit=600 
431          self.OutputFlag=0  
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           
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           
467          ret= "duration "+str(self.duration)+"\n" 
468           
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           
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           
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)]={}  
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   
 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=[]   
 602               
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:  
609              ret+="mode " 
610              ret+=str(self.modes[0])  
611          elif len(self.modes)>=2: 
612              for m in self.modes:  
613                  ret+=str(m.name) +" "  
614              ret+="\n" 
615          else: 
616              ret+="no mode \n" 
617          return ret 
 618   
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) 
 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               
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   
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           
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([]):  
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   
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 
 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   
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