Chris@87
|
1 """
|
Chris@87
|
2 Utililty classes and functions for the polynomial modules.
|
Chris@87
|
3
|
Chris@87
|
4 This module provides: error and warning objects; a polynomial base class;
|
Chris@87
|
5 and some routines used in both the `polynomial` and `chebyshev` modules.
|
Chris@87
|
6
|
Chris@87
|
7 Error objects
|
Chris@87
|
8 -------------
|
Chris@87
|
9
|
Chris@87
|
10 .. autosummary::
|
Chris@87
|
11 :toctree: generated/
|
Chris@87
|
12
|
Chris@87
|
13 PolyError base class for this sub-package's errors.
|
Chris@87
|
14 PolyDomainError raised when domains are mismatched.
|
Chris@87
|
15
|
Chris@87
|
16 Warning objects
|
Chris@87
|
17 ---------------
|
Chris@87
|
18
|
Chris@87
|
19 .. autosummary::
|
Chris@87
|
20 :toctree: generated/
|
Chris@87
|
21
|
Chris@87
|
22 RankWarning raised in least-squares fit for rank-deficient matrix.
|
Chris@87
|
23
|
Chris@87
|
24 Base class
|
Chris@87
|
25 ----------
|
Chris@87
|
26
|
Chris@87
|
27 .. autosummary::
|
Chris@87
|
28 :toctree: generated/
|
Chris@87
|
29
|
Chris@87
|
30 PolyBase Obsolete base class for the polynomial classes. Do not use.
|
Chris@87
|
31
|
Chris@87
|
32 Functions
|
Chris@87
|
33 ---------
|
Chris@87
|
34
|
Chris@87
|
35 .. autosummary::
|
Chris@87
|
36 :toctree: generated/
|
Chris@87
|
37
|
Chris@87
|
38 as_series convert list of array_likes into 1-D arrays of common type.
|
Chris@87
|
39 trimseq remove trailing zeros.
|
Chris@87
|
40 trimcoef remove small trailing coefficients.
|
Chris@87
|
41 getdomain return the domain appropriate for a given set of abscissae.
|
Chris@87
|
42 mapdomain maps points between domains.
|
Chris@87
|
43 mapparms parameters of the linear map between domains.
|
Chris@87
|
44
|
Chris@87
|
45 """
|
Chris@87
|
46 from __future__ import division, absolute_import, print_function
|
Chris@87
|
47
|
Chris@87
|
48 import numpy as np
|
Chris@87
|
49
|
Chris@87
|
50 __all__ = [
|
Chris@87
|
51 'RankWarning', 'PolyError', 'PolyDomainError', 'as_series', 'trimseq',
|
Chris@87
|
52 'trimcoef', 'getdomain', 'mapdomain', 'mapparms', 'PolyBase']
|
Chris@87
|
53
|
Chris@87
|
54 #
|
Chris@87
|
55 # Warnings and Exceptions
|
Chris@87
|
56 #
|
Chris@87
|
57
|
Chris@87
|
58 class RankWarning(UserWarning):
|
Chris@87
|
59 """Issued by chebfit when the design matrix is rank deficient."""
|
Chris@87
|
60 pass
|
Chris@87
|
61
|
Chris@87
|
62 class PolyError(Exception):
|
Chris@87
|
63 """Base class for errors in this module."""
|
Chris@87
|
64 pass
|
Chris@87
|
65
|
Chris@87
|
66 class PolyDomainError(PolyError):
|
Chris@87
|
67 """Issued by the generic Poly class when two domains don't match.
|
Chris@87
|
68
|
Chris@87
|
69 This is raised when an binary operation is passed Poly objects with
|
Chris@87
|
70 different domains.
|
Chris@87
|
71
|
Chris@87
|
72 """
|
Chris@87
|
73 pass
|
Chris@87
|
74
|
Chris@87
|
75 #
|
Chris@87
|
76 # Base class for all polynomial types
|
Chris@87
|
77 #
|
Chris@87
|
78
|
Chris@87
|
79 class PolyBase(object):
|
Chris@87
|
80 """
|
Chris@87
|
81 Base class for all polynomial types.
|
Chris@87
|
82
|
Chris@87
|
83 Deprecated in numpy 1.9.0, use the abstract
|
Chris@87
|
84 ABCPolyBase class instead. Note that the latter
|
Chris@87
|
85 reguires a number of virtual functions to be
|
Chris@87
|
86 implemented.
|
Chris@87
|
87
|
Chris@87
|
88 """
|
Chris@87
|
89 pass
|
Chris@87
|
90
|
Chris@87
|
91 #
|
Chris@87
|
92 # Helper functions to convert inputs to 1-D arrays
|
Chris@87
|
93 #
|
Chris@87
|
94 def trimseq(seq):
|
Chris@87
|
95 """Remove small Poly series coefficients.
|
Chris@87
|
96
|
Chris@87
|
97 Parameters
|
Chris@87
|
98 ----------
|
Chris@87
|
99 seq : sequence
|
Chris@87
|
100 Sequence of Poly series coefficients. This routine fails for
|
Chris@87
|
101 empty sequences.
|
Chris@87
|
102
|
Chris@87
|
103 Returns
|
Chris@87
|
104 -------
|
Chris@87
|
105 series : sequence
|
Chris@87
|
106 Subsequence with trailing zeros removed. If the resulting sequence
|
Chris@87
|
107 would be empty, return the first element. The returned sequence may
|
Chris@87
|
108 or may not be a view.
|
Chris@87
|
109
|
Chris@87
|
110 Notes
|
Chris@87
|
111 -----
|
Chris@87
|
112 Do not lose the type info if the sequence contains unknown objects.
|
Chris@87
|
113
|
Chris@87
|
114 """
|
Chris@87
|
115 if len(seq) == 0:
|
Chris@87
|
116 return seq
|
Chris@87
|
117 else:
|
Chris@87
|
118 for i in range(len(seq) - 1, -1, -1):
|
Chris@87
|
119 if seq[i] != 0:
|
Chris@87
|
120 break
|
Chris@87
|
121 return seq[:i+1]
|
Chris@87
|
122
|
Chris@87
|
123
|
Chris@87
|
124 def as_series(alist, trim=True):
|
Chris@87
|
125 """
|
Chris@87
|
126 Return argument as a list of 1-d arrays.
|
Chris@87
|
127
|
Chris@87
|
128 The returned list contains array(s) of dtype double, complex double, or
|
Chris@87
|
129 object. A 1-d argument of shape ``(N,)`` is parsed into ``N`` arrays of
|
Chris@87
|
130 size one; a 2-d argument of shape ``(M,N)`` is parsed into ``M`` arrays
|
Chris@87
|
131 of size ``N`` (i.e., is "parsed by row"); and a higher dimensional array
|
Chris@87
|
132 raises a Value Error if it is not first reshaped into either a 1-d or 2-d
|
Chris@87
|
133 array.
|
Chris@87
|
134
|
Chris@87
|
135 Parameters
|
Chris@87
|
136 ----------
|
Chris@87
|
137 a : array_like
|
Chris@87
|
138 A 1- or 2-d array_like
|
Chris@87
|
139 trim : boolean, optional
|
Chris@87
|
140 When True, trailing zeros are removed from the inputs.
|
Chris@87
|
141 When False, the inputs are passed through intact.
|
Chris@87
|
142
|
Chris@87
|
143 Returns
|
Chris@87
|
144 -------
|
Chris@87
|
145 [a1, a2,...] : list of 1-D arrays
|
Chris@87
|
146 A copy of the input data as a list of 1-d arrays.
|
Chris@87
|
147
|
Chris@87
|
148 Raises
|
Chris@87
|
149 ------
|
Chris@87
|
150 ValueError
|
Chris@87
|
151 Raised when `as_series` cannot convert its input to 1-d arrays, or at
|
Chris@87
|
152 least one of the resulting arrays is empty.
|
Chris@87
|
153
|
Chris@87
|
154 Examples
|
Chris@87
|
155 --------
|
Chris@87
|
156 >>> from numpy import polynomial as P
|
Chris@87
|
157 >>> a = np.arange(4)
|
Chris@87
|
158 >>> P.as_series(a)
|
Chris@87
|
159 [array([ 0.]), array([ 1.]), array([ 2.]), array([ 3.])]
|
Chris@87
|
160 >>> b = np.arange(6).reshape((2,3))
|
Chris@87
|
161 >>> P.as_series(b)
|
Chris@87
|
162 [array([ 0., 1., 2.]), array([ 3., 4., 5.])]
|
Chris@87
|
163
|
Chris@87
|
164 """
|
Chris@87
|
165 arrays = [np.array(a, ndmin=1, copy=0) for a in alist]
|
Chris@87
|
166 if min([a.size for a in arrays]) == 0:
|
Chris@87
|
167 raise ValueError("Coefficient array is empty")
|
Chris@87
|
168 if any([a.ndim != 1 for a in arrays]):
|
Chris@87
|
169 raise ValueError("Coefficient array is not 1-d")
|
Chris@87
|
170 if trim:
|
Chris@87
|
171 arrays = [trimseq(a) for a in arrays]
|
Chris@87
|
172
|
Chris@87
|
173 if any([a.dtype == np.dtype(object) for a in arrays]):
|
Chris@87
|
174 ret = []
|
Chris@87
|
175 for a in arrays:
|
Chris@87
|
176 if a.dtype != np.dtype(object):
|
Chris@87
|
177 tmp = np.empty(len(a), dtype=np.dtype(object))
|
Chris@87
|
178 tmp[:] = a[:]
|
Chris@87
|
179 ret.append(tmp)
|
Chris@87
|
180 else:
|
Chris@87
|
181 ret.append(a.copy())
|
Chris@87
|
182 else:
|
Chris@87
|
183 try:
|
Chris@87
|
184 dtype = np.common_type(*arrays)
|
Chris@87
|
185 except:
|
Chris@87
|
186 raise ValueError("Coefficient arrays have no common type")
|
Chris@87
|
187 ret = [np.array(a, copy=1, dtype=dtype) for a in arrays]
|
Chris@87
|
188 return ret
|
Chris@87
|
189
|
Chris@87
|
190
|
Chris@87
|
191 def trimcoef(c, tol=0):
|
Chris@87
|
192 """
|
Chris@87
|
193 Remove "small" "trailing" coefficients from a polynomial.
|
Chris@87
|
194
|
Chris@87
|
195 "Small" means "small in absolute value" and is controlled by the
|
Chris@87
|
196 parameter `tol`; "trailing" means highest order coefficient(s), e.g., in
|
Chris@87
|
197 ``[0, 1, 1, 0, 0]`` (which represents ``0 + x + x**2 + 0*x**3 + 0*x**4``)
|
Chris@87
|
198 both the 3-rd and 4-th order coefficients would be "trimmed."
|
Chris@87
|
199
|
Chris@87
|
200 Parameters
|
Chris@87
|
201 ----------
|
Chris@87
|
202 c : array_like
|
Chris@87
|
203 1-d array of coefficients, ordered from lowest order to highest.
|
Chris@87
|
204 tol : number, optional
|
Chris@87
|
205 Trailing (i.e., highest order) elements with absolute value less
|
Chris@87
|
206 than or equal to `tol` (default value is zero) are removed.
|
Chris@87
|
207
|
Chris@87
|
208 Returns
|
Chris@87
|
209 -------
|
Chris@87
|
210 trimmed : ndarray
|
Chris@87
|
211 1-d array with trailing zeros removed. If the resulting series
|
Chris@87
|
212 would be empty, a series containing a single zero is returned.
|
Chris@87
|
213
|
Chris@87
|
214 Raises
|
Chris@87
|
215 ------
|
Chris@87
|
216 ValueError
|
Chris@87
|
217 If `tol` < 0
|
Chris@87
|
218
|
Chris@87
|
219 See Also
|
Chris@87
|
220 --------
|
Chris@87
|
221 trimseq
|
Chris@87
|
222
|
Chris@87
|
223 Examples
|
Chris@87
|
224 --------
|
Chris@87
|
225 >>> from numpy import polynomial as P
|
Chris@87
|
226 >>> P.trimcoef((0,0,3,0,5,0,0))
|
Chris@87
|
227 array([ 0., 0., 3., 0., 5.])
|
Chris@87
|
228 >>> P.trimcoef((0,0,1e-3,0,1e-5,0,0),1e-3) # item == tol is trimmed
|
Chris@87
|
229 array([ 0.])
|
Chris@87
|
230 >>> i = complex(0,1) # works for complex
|
Chris@87
|
231 >>> P.trimcoef((3e-4,1e-3*(1-i),5e-4,2e-5*(1+i)), 1e-3)
|
Chris@87
|
232 array([ 0.0003+0.j , 0.0010-0.001j])
|
Chris@87
|
233
|
Chris@87
|
234 """
|
Chris@87
|
235 if tol < 0:
|
Chris@87
|
236 raise ValueError("tol must be non-negative")
|
Chris@87
|
237
|
Chris@87
|
238 [c] = as_series([c])
|
Chris@87
|
239 [ind] = np.where(np.abs(c) > tol)
|
Chris@87
|
240 if len(ind) == 0:
|
Chris@87
|
241 return c[:1]*0
|
Chris@87
|
242 else:
|
Chris@87
|
243 return c[:ind[-1] + 1].copy()
|
Chris@87
|
244
|
Chris@87
|
245 def getdomain(x):
|
Chris@87
|
246 """
|
Chris@87
|
247 Return a domain suitable for given abscissae.
|
Chris@87
|
248
|
Chris@87
|
249 Find a domain suitable for a polynomial or Chebyshev series
|
Chris@87
|
250 defined at the values supplied.
|
Chris@87
|
251
|
Chris@87
|
252 Parameters
|
Chris@87
|
253 ----------
|
Chris@87
|
254 x : array_like
|
Chris@87
|
255 1-d array of abscissae whose domain will be determined.
|
Chris@87
|
256
|
Chris@87
|
257 Returns
|
Chris@87
|
258 -------
|
Chris@87
|
259 domain : ndarray
|
Chris@87
|
260 1-d array containing two values. If the inputs are complex, then
|
Chris@87
|
261 the two returned points are the lower left and upper right corners
|
Chris@87
|
262 of the smallest rectangle (aligned with the axes) in the complex
|
Chris@87
|
263 plane containing the points `x`. If the inputs are real, then the
|
Chris@87
|
264 two points are the ends of the smallest interval containing the
|
Chris@87
|
265 points `x`.
|
Chris@87
|
266
|
Chris@87
|
267 See Also
|
Chris@87
|
268 --------
|
Chris@87
|
269 mapparms, mapdomain
|
Chris@87
|
270
|
Chris@87
|
271 Examples
|
Chris@87
|
272 --------
|
Chris@87
|
273 >>> from numpy.polynomial import polyutils as pu
|
Chris@87
|
274 >>> points = np.arange(4)**2 - 5; points
|
Chris@87
|
275 array([-5, -4, -1, 4])
|
Chris@87
|
276 >>> pu.getdomain(points)
|
Chris@87
|
277 array([-5., 4.])
|
Chris@87
|
278 >>> c = np.exp(complex(0,1)*np.pi*np.arange(12)/6) # unit circle
|
Chris@87
|
279 >>> pu.getdomain(c)
|
Chris@87
|
280 array([-1.-1.j, 1.+1.j])
|
Chris@87
|
281
|
Chris@87
|
282 """
|
Chris@87
|
283 [x] = as_series([x], trim=False)
|
Chris@87
|
284 if x.dtype.char in np.typecodes['Complex']:
|
Chris@87
|
285 rmin, rmax = x.real.min(), x.real.max()
|
Chris@87
|
286 imin, imax = x.imag.min(), x.imag.max()
|
Chris@87
|
287 return np.array((complex(rmin, imin), complex(rmax, imax)))
|
Chris@87
|
288 else:
|
Chris@87
|
289 return np.array((x.min(), x.max()))
|
Chris@87
|
290
|
Chris@87
|
291 def mapparms(old, new):
|
Chris@87
|
292 """
|
Chris@87
|
293 Linear map parameters between domains.
|
Chris@87
|
294
|
Chris@87
|
295 Return the parameters of the linear map ``offset + scale*x`` that maps
|
Chris@87
|
296 `old` to `new` such that ``old[i] -> new[i]``, ``i = 0, 1``.
|
Chris@87
|
297
|
Chris@87
|
298 Parameters
|
Chris@87
|
299 ----------
|
Chris@87
|
300 old, new : array_like
|
Chris@87
|
301 Domains. Each domain must (successfully) convert to a 1-d array
|
Chris@87
|
302 containing precisely two values.
|
Chris@87
|
303
|
Chris@87
|
304 Returns
|
Chris@87
|
305 -------
|
Chris@87
|
306 offset, scale : scalars
|
Chris@87
|
307 The map ``L(x) = offset + scale*x`` maps the first domain to the
|
Chris@87
|
308 second.
|
Chris@87
|
309
|
Chris@87
|
310 See Also
|
Chris@87
|
311 --------
|
Chris@87
|
312 getdomain, mapdomain
|
Chris@87
|
313
|
Chris@87
|
314 Notes
|
Chris@87
|
315 -----
|
Chris@87
|
316 Also works for complex numbers, and thus can be used to calculate the
|
Chris@87
|
317 parameters required to map any line in the complex plane to any other
|
Chris@87
|
318 line therein.
|
Chris@87
|
319
|
Chris@87
|
320 Examples
|
Chris@87
|
321 --------
|
Chris@87
|
322 >>> from numpy import polynomial as P
|
Chris@87
|
323 >>> P.mapparms((-1,1),(-1,1))
|
Chris@87
|
324 (0.0, 1.0)
|
Chris@87
|
325 >>> P.mapparms((1,-1),(-1,1))
|
Chris@87
|
326 (0.0, -1.0)
|
Chris@87
|
327 >>> i = complex(0,1)
|
Chris@87
|
328 >>> P.mapparms((-i,-1),(1,i))
|
Chris@87
|
329 ((1+1j), (1+0j))
|
Chris@87
|
330
|
Chris@87
|
331 """
|
Chris@87
|
332 oldlen = old[1] - old[0]
|
Chris@87
|
333 newlen = new[1] - new[0]
|
Chris@87
|
334 off = (old[1]*new[0] - old[0]*new[1])/oldlen
|
Chris@87
|
335 scl = newlen/oldlen
|
Chris@87
|
336 return off, scl
|
Chris@87
|
337
|
Chris@87
|
338 def mapdomain(x, old, new):
|
Chris@87
|
339 """
|
Chris@87
|
340 Apply linear map to input points.
|
Chris@87
|
341
|
Chris@87
|
342 The linear map ``offset + scale*x`` that maps the domain `old` to
|
Chris@87
|
343 the domain `new` is applied to the points `x`.
|
Chris@87
|
344
|
Chris@87
|
345 Parameters
|
Chris@87
|
346 ----------
|
Chris@87
|
347 x : array_like
|
Chris@87
|
348 Points to be mapped. If `x` is a subtype of ndarray the subtype
|
Chris@87
|
349 will be preserved.
|
Chris@87
|
350 old, new : array_like
|
Chris@87
|
351 The two domains that determine the map. Each must (successfully)
|
Chris@87
|
352 convert to 1-d arrays containing precisely two values.
|
Chris@87
|
353
|
Chris@87
|
354 Returns
|
Chris@87
|
355 -------
|
Chris@87
|
356 x_out : ndarray
|
Chris@87
|
357 Array of points of the same shape as `x`, after application of the
|
Chris@87
|
358 linear map between the two domains.
|
Chris@87
|
359
|
Chris@87
|
360 See Also
|
Chris@87
|
361 --------
|
Chris@87
|
362 getdomain, mapparms
|
Chris@87
|
363
|
Chris@87
|
364 Notes
|
Chris@87
|
365 -----
|
Chris@87
|
366 Effectively, this implements:
|
Chris@87
|
367
|
Chris@87
|
368 .. math ::
|
Chris@87
|
369 x\\_out = new[0] + m(x - old[0])
|
Chris@87
|
370
|
Chris@87
|
371 where
|
Chris@87
|
372
|
Chris@87
|
373 .. math ::
|
Chris@87
|
374 m = \\frac{new[1]-new[0]}{old[1]-old[0]}
|
Chris@87
|
375
|
Chris@87
|
376 Examples
|
Chris@87
|
377 --------
|
Chris@87
|
378 >>> from numpy import polynomial as P
|
Chris@87
|
379 >>> old_domain = (-1,1)
|
Chris@87
|
380 >>> new_domain = (0,2*np.pi)
|
Chris@87
|
381 >>> x = np.linspace(-1,1,6); x
|
Chris@87
|
382 array([-1. , -0.6, -0.2, 0.2, 0.6, 1. ])
|
Chris@87
|
383 >>> x_out = P.mapdomain(x, old_domain, new_domain); x_out
|
Chris@87
|
384 array([ 0. , 1.25663706, 2.51327412, 3.76991118, 5.02654825,
|
Chris@87
|
385 6.28318531])
|
Chris@87
|
386 >>> x - P.mapdomain(x_out, new_domain, old_domain)
|
Chris@87
|
387 array([ 0., 0., 0., 0., 0., 0.])
|
Chris@87
|
388
|
Chris@87
|
389 Also works for complex numbers (and thus can be used to map any line in
|
Chris@87
|
390 the complex plane to any other line therein).
|
Chris@87
|
391
|
Chris@87
|
392 >>> i = complex(0,1)
|
Chris@87
|
393 >>> old = (-1 - i, 1 + i)
|
Chris@87
|
394 >>> new = (-1 + i, 1 - i)
|
Chris@87
|
395 >>> z = np.linspace(old[0], old[1], 6); z
|
Chris@87
|
396 array([-1.0-1.j , -0.6-0.6j, -0.2-0.2j, 0.2+0.2j, 0.6+0.6j, 1.0+1.j ])
|
Chris@87
|
397 >>> new_z = P.mapdomain(z, old, new); new_z
|
Chris@87
|
398 array([-1.0+1.j , -0.6+0.6j, -0.2+0.2j, 0.2-0.2j, 0.6-0.6j, 1.0-1.j ])
|
Chris@87
|
399
|
Chris@87
|
400 """
|
Chris@87
|
401 x = np.asanyarray(x)
|
Chris@87
|
402 off, scl = mapparms(old, new)
|
Chris@87
|
403 return off + scl*x
|