ATLAS Offline Software
interpolate.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
2 
3 #
4 # File: interpolate.py
5 # Created: sss, 2004.
6 # Purpose: Polynomial interpolation in a table.
7 #
8 
9 
10 """
11 Polynomial interpolation in a table.
12 This is a python version of CaloClusterCorrection/interpolate.
13 """
14 
15 # Helper: do a binary search in a table.
16 # Note: these are slightly different than the standard python definition
17 # of bisect, in that the comparison is done with <= rather than <.
18 # This results in different behavior for the case that a key exactly
19 # matching x is present in the table. As it's done here, it matches
20 # the result of the C++ lower_bound function, used in the C++ implementation.
21 def _bisect (a, x, lo=0, hi=None):
22  if hi is None:
23  hi = len(a)
24  while lo < hi:
25  mid = (lo+hi)//2
26  if x <= a[mid]:
27  hi = mid
28  else:
29  lo = mid+1
30  return lo
31 
32 # Helper: do a binary search in a 2D table.
33 def _bisect2 (a, x, xcol, lo=0, hi=None):
34  if hi is None:
35  hi = len(a)
36  while lo < hi:
37  mid = (lo+hi)//2
38  if x <= a[mid][xcol]:
39  hi = mid
40  else:
41  lo = mid+1
42  return lo
43 
44 
45 #
46 # Polynomial interpolation in a table.
47 #
48 def interpolate (points, x, degree, ycol=1, regions=[], xcol=0):
49  """Polynomial interpolation in a table.
50 
51  points Interpolation table.
52  The x values are in the first column (column 0),
53  and by default, the y values are in the second column (column 1).
54  The x values must be in ascending order, with no duplicates.
55  x The value to interpolate.
56  degree The degree of the interpolating polynomial.
57  ycol The column number of the y values. (0-based.)
58  regions Sometimes, you want to divide the range being interpolated
59  into several distinct regions, where the interpolated
60  function may be discontinuous at the boundaries
61  between regions. To do this, supply this argument,
62  which should contain a list (in ascending order)
63  of the x-coordinates of the region boundaries.
64  When the interpolation runs against a boundary,
65  the algorithm will add a copy of the last point
66  before the boundary, positioned at the boundary.
67  This helps to control runaway extrapolation
68  leading up to the boundary.
69  Returns the interpolated value.
70 
71  The method used is Newtonian interpolation.
72  Based on the cernlib routine divdif.
73  """
74  if len (points) < 2 or degree < 1:
75  raise Exception ('bad args!')
76  degree = min (degree, len (points) - 1)
77 
78 
79  # START. FIND SUBSCRIPT IX OF X IN ARRAY.
80  #ix = _bisect (points, x, lambda x, p, xcol=xcol: cmp (x, p[xcol]))# - 1
81  ix = _bisect2 (points, x, xcol)
82 
83  ir = _bisect (regions, x)
84 
85  # Number of points to try for.
86  # Either degree+1 or degree+2, whichever is even,
87  # to give the same number of points on each side.
88  # If we run up against an edge or a boundary, we'll
89  # fall back to using just degree+1 points (or fewer if we can't
90  # even get that many).
91  # If we end up using degree+2 points, we'll do two interpolations
92  # of degree degree and average them.
93  npts = degree + 2 - (degree%2)
94  l = 0 # noqa: E741
95  t = []
96  d = []
97 
98  # If we run up against the edge of a region boundary,
99  # we'll want to add a psuedopoint right at the boundary
100  # (copying the point closest to the boundary) instead of the
101  # point farthest away from it.
102  extralo = 0
103  extrahi = 0
104 
105  # Starting point index, not considering edges or boundaries.
106  ilo = ix - npts // 2
107 
108  # Make sure this point is within the array range and has not
109  # crossed a region boundary.
110  if ilo < 0:
111  ilo = 0
112  npts = degree+1
113  while ilo < len (points) and ir>0 and points[ilo][xcol] < regions[ir-1]:
114  ilo += 1
115  npts = degree+1
116  extralo = 1
117 
118  # Same deal for the right hand edge.
119  # ihi is one past the last point to use.
120  himoved = 0
121  ihi = ilo + npts
122  if ihi > len (points):
123  ihi = len (points)
124  npts = degree+1
125  himoved = 1
126  while ihi > 0 and ir<len(regions) and points[ihi-1][xcol] >= regions[ir]:
127  ihi -= 1
128  npts = degree+1
129  extrahi = 1
130  himoved = 1
131 
132  lomoved = 0
133  ilo = ihi - npts
134  if ilo < 0:
135  ilo = 0
136  lomoved = 1
137  while ilo < len (points) and ir>0 and points[ilo][xcol] < regions[ir-1]:
138  ilo += 1
139  extralo = 1
140  lomoved = 1
141 
142  npts = ihi - ilo
143  t = []
144  d = []
145 
146  if extralo and points[ilo][xcol] != regions[ir-1]:
147  if not himoved:
148  ihi -= 1
149  else:
150  npts += 1
151  t.append (regions[ir-1])
152  d.append (points[ilo][ycol])
153 
154  if extrahi and points[ihi-1][xcol] != regions[ir]:
155  if not lomoved:
156  ilo += 1
157  else:
158  npts += 1
159  t.append (regions[ir])
160  d.append (points[ihi-1][ycol])
161 
162  t += [points[i][xcol] for i in range (ilo, ihi)]
163  d += [points[i][ycol] for i in range (ilo, ihi)]
164 
165  degree = min (degree, npts-1)
166  extra = npts != degree+1
167 
168  if extra:
169  (t[0], t[npts-2]) = (t[npts-2], t[0])
170  (d[0], d[npts-2]) = (d[npts-2], d[0])
171 
172  # REPLACE D BY THE LEADING DIAGONAL OF A DIVIDED-DIFFERENCE TABLE,
173  # SUPPLEMENTED BY AN EXTRA LINE IF *EXTRA* IS TRUE.
174  for l in range(0, degree):
175  if extra:
176  d[degree+1] = (d[degree+1]-d[degree-1]) / (t[degree+1]-t[degree-1-l])
177  for i in range (degree, l, -1):
178  d[i] = (d[i]-d[i-1]) / (t[i]-t[i-1-l])
179 
180  # EVALUATE THE NEWTON INTERPOLATION FORMULA AT X, AVERAGING TWO VALUES
181  # OF LAST DIFFERENCE IF *EXTRA* IS TRUE.
182  sum = d[degree]
183  if extra:
184  sum=0.5*(sum+d[degree+1])
185  for j in range (degree-1, -1, -1):
186  sum = d[j] + (x - t[j]) * sum
187 
188  return sum
189 
190 
191 __test__ = {}
192 __test__['tests'] = """
193 >>> def is_different (a, b):
194 ... den = abs(a) + abs(b)
195 ... if den == 0: return 0
196 ... return abs((a-b)/den) > 1e-6
197 >>> def testit (y2, table, x, degree, regions=[], ycol=1):
198 ... y1 = interpolate (table, x, degree, ycol, regions)
199 ... if is_different(y1, y2):
200 ... return "diff %f %f %f" % (x, y1, y2)
201 >>> table = [
202 ... [0.05, 6.264762e-01],
203 ... [0.15, 6.671484e-01],
204 ... [0.25, 7.134157e-01],
205 ... [0.35, 8.211740e-01],
206 ... [0.45, 9.047978e-01],
207 ... [0.55, 9.087084e-01],
208 ... [0.65, 8.923957e-01],
209 ... [0.75, 9.584507e-01],
210 ... [0.85, -5.659607e-01],
211 ... [0.95, -6.995252e-01],
212 ... [1.05, -8.711259e-01],
213 ... [1.15, -1.038724e+00],
214 ... [1.25, -1.294152e+00],
215 ... [1.35, -1.170352e+00],
216 ... [1.45, -9.312256e-01],
217 ... ]
218 >>> testit ( 0.912999, table, 0.5, 3);
219 >>> testit ( 0.590771, table, 0.0, 3);
220 >>> testit (-0.871126, table, 1.05, 3);
221 >>> testit (-1.0011166, table, 1.13, 3);
222 >>> testit (-1.04871, table, 1.4 , 3);
223 >>> testit (-9.34768, table, 2.0 , 3);
224 >>>
225 >>> testit ( 0.91485268, table, 0.5, 4);
226 >>> testit ( 0.55207348, table, 0.0, 4);
227 >>> testit (-0.87112588, table, 1.05, 4);
228 >>> testit (-0.99805647, table, 1.13, 4);
229 >>> testit (-1.0201578, table, 1.4 , 4);
230 >>> testit (-78.760239, table, 2.0 , 4);
231 >>>
232 >>> regions = [0.8, 1.05]
233 >>> testit ( 0.65513462, table, 0.1, 4, regions);
234 >>> testit ( 0.92242599, table, 0.7, 4, regions);
235 >>> testit (-0.88620204, table, 1.1, 4, regions);
236 >>> testit (-0.73959452, table, 1.0, 4, regions);
237 >>> testit (-0.69952518, table, 1.05, 4, regions);
238 >>> testit ( 0.95845068, table, 0.8, 4, regions);
239 >>> testit (-1.2802936, table, 1.3, 4, regions);
240 >>>
241 >>> testit ( 0.64960641, table, 0.1, 3, regions);
242 >>> testit ( 0.92791027, table, 0.7, 3, regions);
243 >>> testit (-0.91475517, table, 1.1, 3, regions);
244 >>> testit (-0.73959452, table, 1.0, 3, regions);
245 >>> testit (-0.69952518, table, 1.05, 3, regions);
246 >>> testit ( 0.95845068, table, 0.8, 3, regions);
247 >>> testit (-1.2631618, table, 1.3, 3, regions);
248 >>>
249 >>> testit (0.1, table, 0.1, 3, regions, 0);
250 >>>
251 >>> regions2 = [0.2, 1.2]
252 >>> testit ( 0.65359104, table, 0.1, 4, regions2);
253 >>> testit (-1.2503984, table, 1.3, 4, regions2);
254 """
255 
256 
257 if __name__ == "__main__":
258  print ('PyAnalysisUtils/interpolate.py test')
259  import doctest
260  doctest.testmod()
python.interpolate._bisect
def _bisect(a, x, lo=0, hi=None)
Definition: interpolate.py:21
python.interpolate._bisect2
def _bisect2(a, x, xcol, lo=0, hi=None)
Definition: interpolate.py:33
python.interpolate.interpolate
def interpolate(points, x, degree, ycol=1, regions=[], xcol=0)
Definition: interpolate.py:48
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:194