Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALBurst 2.0.7.1-ea7c608
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Macros Modules Pages
timeslides.py
Go to the documentation of this file.
1# Copyright (C) 2006--2018 Kipp Cannon
2#
3# This program is free software; you can redistribute it and/or modify it
4# under the terms of the GNU General Public License as published by the
5# Free Software Foundation; either version 2 of the License, or (at your
6# option) any later version.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11# Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program; if not, write to the Free Software Foundation, Inc.,
15# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
17
18#
19# =============================================================================
20#
21# Preamble
22#
23# =============================================================================
24#
25
26
27import itertools
28from tqdm import tqdm
29
30
31from . import offsetvector
32
33
34__author__ = "Kipp Cannon <kipp.cannon@ligo.org>"
35from .git_version import date as __date__
36from .git_version import version as __version__
37
38
39#
40# =============================================================================
41#
42# Command Line Parsing
43#
44# =============================================================================
45#
46
47
48def parse_slidespec(slidespec):
49 """
50 Accepts a string in the format
51 instrument=first:last:step[,first:last:step]... and returns the
52 tuple (instrument, [offset1, offset2, ....]) where the offsets are
53 the sorted list of unique numbers described by the ranges. A range
54 with a positive step describes the offsets (first + n * step) where
55 n is an integer such that first <= offset <= last. A range with a
56 negative step describes the offsets (first + n * step) where n is
57 an integer such that last <= offset <= first.
58
59 Example:
60
61 >>> parse_slidespec("H1=-5:+5:0.5")
62 ('H1', [-5.0, -4.5, -4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0])
63 >>> parse_slidespec("H1=+5:-5:-0.5")
64 ('H1', [-5.0, -4.5, -4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0])
65 >>> parse_slidespec("H1=-5:+5:8")
66 ('H1', [-5.0, 3.0])
67 """
68 try:
69 instrument, rangespec = slidespec.split("=")
70 except ValueError:
71 raise ValueError("cannot parse time slide '%s'" % slidespec)
72 offsets = set()
73 for rng in [s.strip() for s in rangespec.split(",")]:
74 try:
75 first, last, step = map(float, rng.split(":"))
76 except ValueError:
77 raise ValueError("malformed range '%s' in '%s'" % (rng, rangespec))
78 if step == 0:
79 if first != last:
80 raise ValueError("divide by zero in range '%s'" % rng)
81 offsets.add(first)
82 continue
83 elif last > first if step < 0 else last < first:
84 raise ValueError("step has wrong sign in range '%s'" % rng)
85
86 for i in itertools.count():
87 x = first + i * step
88 if not min(first, last) <= x <= max(first, last):
89 break
90 offsets.add(x)
91 return instrument.strip(), sorted(offsets)
92
93
94def parse_slides(slides):
95 """
96 Accepts a list of strings of the format understood by
97 parse_slidespec(), and returns a dictionary mapping instrument
98 names to sorted lists of unique offsets.
99
100 Example:
101
102 >>> parse_slides(["H1=-1:+1:+1", "H2=-1:+1:+1", "L1=0:0:0"])
103 {'H2': [-1.0, 0.0, 1.0], 'H1': [-1.0, 0.0, 1.0], 'L1': [0.0]}
104 """
105 d = {}
106 # store the offsets for each instrument as sets to uniquify the
107 # numbers
108 for slidespec in slides:
109 instrument, offsets = parse_slidespec(slidespec)
110 try:
111 d[instrument] |= set(offsets)
112 except KeyError:
113 d[instrument] = set(offsets)
114 # convert offsets back to sorted lists
115 return dict((instrument, sorted(offsets)) for instrument, offsets in d.items())
116
117
119 """
120 Accepts a string in the format
121 count:instrument=offset[,instrument=offset...] and returns the
122 tuple (count, {instrument: offset, ...})
123
124 Example:
125
126 >>> parse_inspiral_num_slides_slidespec("3:H1=0,H2=5,L1=10")
127 (3, offsetvector({'H2': 5.0, 'H1': 0.0, 'L1': 10.0}))
128 """
129 count, offsets = slidespec.strip().split(":")
130 offsetvect = offsetvector.offsetvector((instrument.strip(), float(offset)) for instrument, offset in (token.strip().split("=") for token in offsets.strip().split(",")))
131 return int(count), offsetvect
132
133
134#
135# =============================================================================
136#
137# Build Time Slides
138#
139# =============================================================================
140#
141
142
143def SlidesIter(slides):
144 """
145 Accepts a dictionary mapping instrument --> list-of-offsets (for
146 example, as returned by parse_slides()), and iterates over the
147 cartesian (outer) product of the offset lists, yielding all
148 possible N-way instrument --> offset mappings.
149
150 Example:
151
152 >>> slides = {"H1": [-1, 0, 1], "H2": [-1, 0, 1], "L1": [0]}
153 >>> list(SlidesIter(slides))
154 [offsetvector({'H2': -1, 'H1': -1, 'L1': 0}), offsetvector({'H2': -1, 'H1': 0, 'L1': 0}), offsetvector({'H2': -1, 'H1': 1, 'L1': 0}), offsetvector({'H2': 0, 'H1': -1, 'L1': 0}), offsetvector({'H2': 0, 'H1': 0, 'L1': 0}), offsetvector({'H2': 0, 'H1': 1, 'L1': 0}), offsetvector({'H2': 1, 'H1': -1, 'L1': 0}), offsetvector({'H2': 1, 'H1': 0, 'L1': 0}), offsetvector({'H2': 1, 'H1': 1, 'L1': 0})]
155 """
156 if not slides:
157 # things get a little odd in the even that no
158 # instrument/offset-list pairs are given. instead of
159 # yielding an empty sequence, itertools.product(*()) yields
160 # a sequence containing a single empty tuple, so instead of
161 # yielding no offsetvectors this function yields one empty
162 # one. that's not what calling codes generally expect the
163 # response to be so we trap the case and return an empty
164 # sequence
165 return
166 instruments = slides.keys()
167 for slide in itertools.product(*slides.values()):
168 yield offsetvector.offsetvector(zip(instruments, slide))
169
170
171def Inspiral_Num_Slides_Iter(count, offsets):
172 """
173 This generator yields a sequence of time slide dictionaries in the
174 style of lalapps_thinca's time slides. Each resulting dictionary
175 maps instrument to offset. The input is a count of time slides (an
176 integer), and a dictionary mapping instrument to offset. The
177 output dictionaries describe time slides that are integer multiples
178 of the input time shifts.
179
180 Example:
181
182 >>> list(Inspiral_Num_Slides_Iter(3, {"H1": 0.0, "H2": 5.0,"L1": 10.0}))
183 [offsetvector({'H2': -15.0, 'H1': -0.0, 'L1': -30.0}), offsetvector({'H2': -10.0, 'H1': -0.0, 'L1': -20.0}), offsetvector({'H2': -5.0, 'H1': -0.0, 'L1': -10.0}), offsetvector({'H2': 0.0, 'H1': 0.0, 'L1': 0.0}), offsetvector({'H2': 5.0, 'H1': 0.0, 'L1': 10.0}), offsetvector({'H2': 10.0, 'H1': 0.0, 'L1': 20.0}), offsetvector({'H2': 15.0, 'H1': 0.0, 'L1': 30.0})]
184
185 The output time slides are all integer multiples of the input time
186 shift vector in the range [-count, +count], and are returned in
187 increasing order of mupltiplier.
188 """
189 offsets = offsets.items()
190 for n in range(-count, +count + 1):
191 yield offsetvector.offsetvector((instrument, offset * n) for instrument, offset in offsets)
192
193
194def vacuum(time_slides, verbose = False):
195 """
196 Given a dictionary mapping time slide ID to offsetvector, for
197 example as returned by the .as_dict() method of the TimeSlideTable
198 class in ligolw.lsctables, construct and return a mapping
199 indicating time slide equivalences. This can be used to delete
200 redundant time slides from a time slide table, and then also used
201 via the .applyKeyMapping() method of ligolw.table.Table instances
202 to update cross references (for example in the coinc_event table).
203
204 Example:
205
206 >>> slides = {0: offsetvector({"H1": 0, "H2": 0}), 1: offsetvector({"H1": 10, "H2": 10}), 2: offsetvector({"H1": 0, "H2": 10})}
207 >>> vacuum(slides)
208 {1: 0}
209
210 indicating that time slide ID 1 describes a time slide that is
211 equivalent to time slide ID 0. The calling code could use this
212 information to delete time slide ID 1 from the time_slide table,
213 and replace references to that ID in other tables with references
214 to time slide ID 0.
215 """
216 # convert offsets to deltas
217 time_slides = dict((time_slide_id, offsetvect.deltas) for time_slide_id, offsetvect in time_slides.items())
218 with tqdm(total = len(time_slides), disable = not verbose) as progressbar:
219 # old --> new mapping
220 mapping = {}
221 # while there are time slide offset dictionaries remaining
222 while time_slides:
223 # pick an ID/offset dictionary pair at random
224 id1, deltas1 = time_slides.popitem()
225 # for every other ID/offset dictionary pair in the
226 # time slides, if the relative offset dictionaries
227 # are equivalent record in the old --> new mapping
228 ids_to_delete = [id2 for id2, deltas2 in time_slides.items() if deltas2 == deltas1]
229 for id2 in ids_to_delete:
230 mapping[id2] = id1
231 time_slides.pop(id2)
232 # number of offset vectors removed from time_slides
233 # in this iteration
234 progressbar.update(1 + len(ids_to_delete))
235 # done
236 return mapping
static double max(double a, double b)
Definition: EPFilters.c:43
static double min(double a, double b)
Definition: EPFilters.c:42
Subclass of the dict built-in type for storing mappings of instrument to time offset.
Definition: offsetvector.py:56
def vacuum(time_slides, verbose=False)
Given a dictionary mapping time slide ID to offsetvector, for example as returned by the ....
Definition: timeslides.py:215
def Inspiral_Num_Slides_Iter(count, offsets)
This generator yields a sequence of time slide dictionaries in the style of lalapps_thinca's time sli...
Definition: timeslides.py:188
def parse_inspiral_num_slides_slidespec(slidespec)
Accepts a string in the format count:instrument=offset[,instrument=offset...] and returns the tuple (...
Definition: timeslides.py:128
def parse_slides(slides)
Accepts a list of strings of the format understood by parse_slidespec(), and returns a dictionary map...
Definition: timeslides.py:104
def SlidesIter(slides)
Accepts a dictionary mapping instrument --> list-of-offsets (for example, as returned by parse_slides...
Definition: timeslides.py:155
def parse_slidespec(slidespec)
Accepts a string in the format instrument=first:last:step[,first:last:step]... and returns the tuple ...
Definition: timeslides.py:67