1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 import itertools
28
29
30 from glue import git_version
31
32
33 __author__ = "Kipp Cannon <kipp.cannon@ligo.org>"
34 __version__ = "git id %s" % git_version.id
35 __date__ = git_version.date
48 """
49 Subclass of the dict built-in type for storing mappings of
50 instrument to time offset.
51
52 Examples:
53
54 >>> x = offsetvector({"H1": 0, "L1": 10, "V1": 20})
55 >>> x["H1"]
56 0
57 >>> not any(x.values()) # check for "zero-lag"
58 False
59
60 The motivation for introducing this class, instead of using
61 dictionaries, is that it provides a number of tools for comparing
62 offset vectors besides strict value-for-value equality. For
63 example the Python cmp() operation compares two offset vectors by
64 the relative offsets between instruments rather than their absolute
65 offsets, whereas the == operation compares two offset vectors by
66 demanding strict equality. There is also the ability to check if
67 one offset vector is a subset of another one.
68 """
69 @property
71 """
72 = min(self)
73
74 Raises ValueError if the offsetvector is empty.
75 """
76
77
78
79
80 if not self:
81 raise ValueError("offsetvector is empty")
82 return min(self)
83
84 @property
86 """
87 Dictionary of relative offsets. The keys in the result are
88 pairs of keys from the offset vector, (a, b), and the
89 values are the relative offsets, (offset[b] - offset[a]).
90 Raises ValueError if the offsetvector is empty (WARNING:
91 this behaviour might change in the future).
92
93 Example:
94
95 >>> x = offsetvector({"H1": 0, "L1": 10, "V1": 20})
96 >>> x.deltas
97 {('H1', 'L1'): 10, ('H1', 'V1'): 20, ('H1', 'H1'): 0}
98 >>> y = offsetvector({'H1': 100, 'L1': 110, 'V1': 120})
99 >>> y.deltas == x.deltas
100 True
101
102 Note that the result always includes a "dummy" entry,
103 giving the relative offset of self.refkey with respect to
104 itself, which is always 0.
105
106 See also .fromdeltas().
107
108 BUGS: I think the keys in each tuple should be reversed.
109 I can't remember why I put them in the way they are.
110 Expect them to change in the future.
111 """
112
113
114
115
116
117
118
119
120
121 refkey = self.refkey
122 refoffset = self[refkey]
123 return dict(((refkey, key), self[key] - refoffset) for key in self)
124
125 - def __str__(self, compact = False):
126 """
127 Return a human-readable string representation of an offset
128 vector.
129
130 Example:
131
132 >>> a = offsetvector({"H1": -10.1234567, "L1": 0.125})
133 >>> str(a)
134 'H1 = -10.1234567 s, L1 = +0.125 s'
135 >>> a.__str__(compact = True)
136 'H1=-10.123,L1=0.125'
137 """
138 if compact:
139 return ",".join(("%s=%.5g" % x) for x in sorted(self.items()))
140 return ", ".join(("%s = %+.16g s" % x) for x in sorted(self.items()))
141
143 """
144 Return a string representation of the offset vector.
145 Running eval() on the result reconstructs the offsetvector.
146
147 Example:
148
149 >>> a = offsetvector({"H1": -10.1234567, "L1": 0.1})
150 >>> repr(a)
151 "offsetvector({'H1': -10.1234567, 'L1': 0.1})"
152 >>> b = eval(repr(a))
153 >>> b
154 offsetvector({'H1': -10.1234567, 'L1': 0.1})
155 >>> b == a
156 True
157 >>> b is a
158 False
159 """
160 return "%s(%s)" % (self.__class__.__name__, dict.__repr__(self))
161
163 """
164 Returns max(offset) - min(offset).
165
166 Example:
167
168 >>> abs(offsetvector({"H1": 0.0, "H2": 0.0, "L1": 0.0}))
169 0.0
170 >>> abs(offsetvector({"H1": 10.0, "H2": 10.0, "L1": 10.0}))
171 0.0
172 >>> abs(offsetvector({'H1': 10.0, 'L1': 0.0, 'V1': -10.0}))
173 20.0
174 """
175 return max(self.values()) - min(self.values())
176
178 """
179 Compare two offset vectors by their relative offsets. The
180 return value is 0 if the relative offsets are all equal,
181 nonzero otherwise.
182
183 Example:
184
185 >>> a = offsetvector({"H1": 0.0, "H2": 0.0, "L1": 0.0})
186 >>> b = offsetvector({"H1": 10.0, "H2": 10.0, "L1": 10.0})
187 >>> cmp(a, b)
188 0
189 >>> a == b
190 False
191
192 Note that cmp() and testing for equality are different
193 tests! The equality test returns False because the offset
194 vectors are not identical, however the cmp() function
195 returns 0 because the relative offsets are all equal.
196 """
197 return cmp(self.deltas, other.deltas)
198
200 """
201 Returns True if offset vector @other can be found in @self,
202 False otherwise. An offset vector is "found in" another
203 offset vector if the latter contains all of the former's
204 instruments and the relative offsets among those
205 instruments are equal (the absolute offsets need not be).
206
207 Example:
208
209 >>> a = offsetvector({"H1": 10, "L1": 20, "V1": 30})
210 >>> b = offsetvector({"H1": 20, "V1": 40})
211 >>> a.contains(b)
212 True
213
214 Note the distinction between this and the "in" operator:
215
216 >>> "H1" in a
217 True
218 """
219 return offsetvector((key, offset) for key, offset in self.items() if key in other).deltas == other.deltas
220
222 """
223 Adjust the offsetvector so that a particular instrument has
224 the desired offset. All other instruments have their
225 offsets adjusted so that the relative offsets are
226 preserved. The instrument to noramlize, and the offset one
227 wishes it to have, are provided as a key-word argument.
228 The return value is the time slide dictionary, which is
229 modified in place.
230
231 If more than one key-word argument is provided the keys are
232 sorted and considered in order until a key is found that is
233 in the offset vector. The offset vector is normalized to
234 that value. This function is a no-op if no key-word
235 argument is found that applies.
236
237 Example:
238
239 >>> a = offsetvector({"H1": -10, "H2": -10, "L1": -10})
240 >>> a.normalize(L1 = 0)
241 offsetvector({'H2': 0, 'H1': 0, 'L1': 0})
242 >>> a = offsetvector({"H1": -10, "H2": -10})
243 >>> a.normalize(L1 = 0, H2 = 5)
244 offsetvector({'H2': 5, 'H1': 5})
245 """
246
247
248 for key, offset in sorted(kwargs.items()):
249 if key in self:
250 delta = offset - self[key]
251 for key in self.keys():
252 self[key] += delta
253 break
254 return self
255
256 @classmethod
258 """
259 Construct an offsetvector from a dictionary of offset
260 deltas as returned by the .deltas attribute.
261
262 Example:
263
264 >>> x = offsetvector({"H1": 0, "L1": 10, "V1": 20})
265 >>> y = offsetvector.fromdeltas(x.deltas)
266 >>> y
267 offsetvector({'V1': 20, 'H1': 0, 'L1': 10})
268 >>> y == x
269 True
270
271 See also .deltas, .fromkeys()
272 """
273 return cls((key, value) for (refkey, key), value in deltas.items())
274
286 """
287 Given an iterable of offset vectors, return the shortest list of
288 the unique n-instrument offset vectors from which all the vectors
289 in the input iterable can be constructed. This can be used to
290 determine the minimal set of n-instrument coincs required to
291 construct all of the coincs for all of the requested instrument and
292 offset combinations in a set of offset vectors.
293
294 It is assumed that the coincs for the vector {"H1": 0, "H2": 10,
295 "L1": 20} can be constructed from the coincs for the vectors {"H1":
296 0, "H2": 10} and {"H2": 0, "L1": 10}, that is only the relative
297 offsets are significant in determining if two events are
298 coincident, not the absolute offsets.
299 """
300
301
302
303
304 delta_sets = {}
305 for vect in offsetvectors:
306 for instruments in itertools.combinations(sorted(vect), n):
307
308
309
310
311
312 delta_sets.setdefault(instruments, set()).add(tuple(vect[instrument] - vect[instruments[0]] for instrument in instruments))
313
314
315
316
317
318 return [offsetvector(zip(instruments, deltas)) for instruments, delta_set in delta_sets.items() for deltas in delta_set]
319