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()
411
412
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
579
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)
632
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
755
756
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
805