88class Timeline :
99 """Identify a timeline on which a port sends or receives.
1010
11- Timeline objects describe when a port sends or receives, either relative to a parent
12- timeline that calls the component they're a part of, or by describing the whole list
13- of components calling each other from the root timeline down.
14-
15- A component c1 that is not called by any other component will have its F_INIT and
16- O_F ports (if any) on the root timeline, which is represented by ':'. If c1's
17- implementation has a loop in which it sends on an O_I port and receives on an S
18- port, then those ports are on a subtimeline, which is named after the component by
19- default, ':c1'.
20-
21- If we add a component c2 and connect its F_INIT and O_F to those ports, then we
22- create a macro-micro type coupling. The F_INIT and O_F ports of c2 will then be on
23- timeline ':c1', because they'll receive and send at the exact points in simulated
24- time that c1's O_I and S ports send and receive.
25-
26- If c2 has its own O_I and S ports, then those will be on timeline ':c1:c2', this
27- being the concatenation of the parent timeline and the relative timeline within c2.
28-
29- Some implementations may have more than one set of O_I/S ports, on which they
30- communicate at different rates. In that case, each port should be given a local
31- relative timeline explicitly. In the above example, if c1's O_I and S ports were
32- specified to be on local relative timeline 'tl1', then their full relative timeline
33- is 'c1.tl' and their absolute timeline is ':c1.tl`, putting c2's O_I and S ports on
34- ':c1.tl:c2' unless they too have an explicit timeline designation.
11+ A timeline is a stretch of time with a beginning and an end that is subdivided into
12+ zero or more time steps that step from one intermediate time point to the next.
13+
14+ F_INIT ports receive at the beginning of the timeline, O_F ports send at the end of
15+ the timeline, O_I ports send at the first and intermediate points on the timeline,
16+ and S ports receive at intermediate and the last time point of the timeline.
17+
18+ Timeline objects are used to annotate components and ports, to describe which
19+ timeline they're on.
20+
21+ For components, the timeline depends on where in a call hierarchy the component
22+ sits. A component ``c1`` that is not called (via an O_I to F_INIT conduit) by any
23+ other model has timeline ``c1``, relative to the model it's in. If ``c2`` is called
24+ by ``c1``, then it is in timeline ``c2`` relative to its *parent timeline* ``c1``,
25+ and in ``c1:c2`` relative to the model. If ``c1`` dispatches (O_F to F_INIT) to
26+ ``c3``, then ``c3``'s parent timeline is the same as ``c1``'s parent timeline, which
27+ is the empty model timeline, putting component ``c3`` into timeline ``c3``.
28+
29+ Ports are on the timeline of the component they're associated with. Some components
30+ however have multiple sets of O_I/S ports, on which they communicate at different
31+ rates. Each group of such ports is annotated, using this class, with a timeline
32+ annotation naming the sub-timeline that port is on. If an O_I port on component
33+ ``c1`` is annotated with ``tl1``, then that port will be on timeline ``c1.tl1``
34+ rather than on ``c1``, and if it were on ``c2`` then the full timeline would be
35+ ``c1:c2.tl1``.
3536
3637 Timelines have a technical representation as a list of References, and a string
3738 representation in which those References are joined using colons.
@@ -41,14 +42,11 @@ class Timeline:
4142 def __init__ (self , timeline : str ) -> None : ...
4243
4344 @overload
44- def __init__ (
45- self , timeline : Sequence [str | Reference ], absolute : bool = True
46- ) -> None : ...
45+ def __init__ (self , timeline : Sequence [str | Reference ]) -> None : ...
4746
4847 def __init__ (
4948 self ,
5049 timeline : str | Sequence [str | Reference ],
51- absolute : bool = True ,
5250 ) -> None :
5351 """Create a Timeline.
5452
@@ -60,32 +58,21 @@ def make_new_reference(x: str | Reference) -> Reference:
6058 return Reference (str (x ))
6159
6260 if isinstance (timeline , str ):
61+ timeline = timeline .strip (":" )
6362 if timeline == "" :
64- self .absolute = False
65- parts : Sequence [str | Reference ] = []
66- elif timeline == ":" :
67- self .absolute = True
68- parts = []
69- else :
70- self .absolute = False
71- if timeline [0 ] == ":" :
72- self .absolute = True
73- timeline = timeline [1 :]
74-
75- parts = timeline .split (":" )
63+ self ._parts = []
64+ return
7665
77- self . _parts = list ( map ( make_new_reference , parts ) )
66+ timeline = timeline . split ( ":" )
7867
79- else :
80- self .absolute = absolute
81- self ._parts = list (map (make_new_reference , timeline ))
68+ self ._parts = list (map (make_new_reference , timeline ))
8269
8370 def __eq__ (self , other : Any ) -> bool :
8471 """Compare with another Timeline or a string for equality."""
8572 if isinstance (other , str ):
8673 return str (self ) == other
8774 elif isinstance (other , Timeline ):
88- return self .absolute == other . absolute and self . _parts == other ._parts
75+ return self ._parts == other ._parts
8976 return NotImplemented
9077
9178 def __hash__ (self ) -> int :
@@ -103,8 +90,7 @@ def __lt__(self, other: Any) -> bool:
10390
10491 def __str__ (self ) -> str :
10592 """Return the string representation of this Timeline."""
106- anchor = ":" if self .absolute else ""
107- return anchor + ":" .join (map (str , self ._parts ))
93+ return ":" .join (map (str , self ._parts ))
10894
10995 def __repr__ (self ) -> str :
11096 """Return a representation of the object."""
@@ -138,16 +124,12 @@ def __iter__(self) -> Iterator[Reference]:
138124 yield from self ._parts
139125
140126 def __add__ (self , other : Any ) -> "Timeline" :
141- """Concatenate this timeline with another (relative!) Timeline."""
127+ """Concatenate this timeline with another Timeline."""
142128 if isinstance (other , Timeline ):
143- if other .absolute :
144- raise ValueError (
145- "Cannot concatenate an absolute Timeline onto another one"
146- )
147- return Timeline (self ._parts + other ._parts , self .absolute )
129+ return Timeline (self ._parts + other ._parts )
148130
149131 if isinstance (other , Reference ):
150- return Timeline (self ._parts + [other ], self . absolute )
132+ return Timeline (self ._parts + [other ])
151133
152134 return NotImplemented
153135
@@ -159,19 +141,16 @@ def parent(self) -> "Timeline":
159141 RuntimeError if this is the root and there is no parent."""
160142 if not self ._parts :
161143 raise RuntimeError ("The root timeline does not have a parent" )
162- return Timeline (self ._parts [:- 1 ], self . absolute )
144+ return Timeline (self ._parts [:- 1 ])
163145
164146 def relative_to (self , other : "Timeline" ) -> "Timeline" :
165147 """Compute a version of this timeline relative to `other`.
166148
167- Both timelines must be absolute, and the other timeline must be a parent
168- timeline of this one.
149+ The other timeline must be a parent timeline of this one.
169150 """
170- if not self .absolute or not other .absolute :
171- raise ValueError ("Both timelines must be absolute" )
172151 if self ._parts [: len (other )] != other ._parts :
173152 raise ValueError (f"{ self } is not a subtimeline of { other } " )
174- return Timeline (self ._parts [len (other ) :], False )
153+ return Timeline (self ._parts [len (other ) :])
175154
176155
177156class Port :
0 commit comments