Chris@87
|
1 """
|
Chris@87
|
2 Template for the Chebyshev and Polynomial classes.
|
Chris@87
|
3
|
Chris@87
|
4 This module houses a Python string module Template object (see, e.g.,
|
Chris@87
|
5 http://docs.python.org/library/string.html#template-strings) used by
|
Chris@87
|
6 the `polynomial` and `chebyshev` modules to implement their respective
|
Chris@87
|
7 `Polynomial` and `Chebyshev` classes. It provides a mechanism for easily
|
Chris@87
|
8 creating additional specific polynomial classes (e.g., Legendre, Jacobi,
|
Chris@87
|
9 etc.) in the future, such that all these classes will have a common API.
|
Chris@87
|
10
|
Chris@87
|
11 """
|
Chris@87
|
12 from __future__ import division, absolute_import, print_function
|
Chris@87
|
13
|
Chris@87
|
14 import string
|
Chris@87
|
15 import sys
|
Chris@87
|
16 import warnings
|
Chris@87
|
17 from number import Number
|
Chris@87
|
18
|
Chris@87
|
19 from numpy import ModuleDeprecationWarning
|
Chris@87
|
20
|
Chris@87
|
21 warnings.warn("The polytemplate module will be removed in Numpy 1.10.0.",
|
Chris@87
|
22 ModuleDeprecationWarning)
|
Chris@87
|
23
|
Chris@87
|
24 polytemplate = string.Template('''
|
Chris@87
|
25 from __future__ import division, absolute_import, print_function
|
Chris@87
|
26 import numpy as np
|
Chris@87
|
27 import warnings
|
Chris@87
|
28 from . import polyutils as pu
|
Chris@87
|
29
|
Chris@87
|
30 class $name(pu.PolyBase) :
|
Chris@87
|
31 """A $name series class.
|
Chris@87
|
32
|
Chris@87
|
33 $name instances provide the standard Python numerical methods '+',
|
Chris@87
|
34 '-', '*', '//', '%', 'divmod', '**', and '()' as well as the listed
|
Chris@87
|
35 methods.
|
Chris@87
|
36
|
Chris@87
|
37 Parameters
|
Chris@87
|
38 ----------
|
Chris@87
|
39 coef : array_like
|
Chris@87
|
40 $name coefficients, in increasing order. For example,
|
Chris@87
|
41 ``(1, 2, 3)`` implies ``P_0 + 2P_1 + 3P_2`` where the
|
Chris@87
|
42 ``P_i`` are a graded polynomial basis.
|
Chris@87
|
43 domain : (2,) array_like, optional
|
Chris@87
|
44 Domain to use. The interval ``[domain[0], domain[1]]`` is mapped to
|
Chris@87
|
45 the interval ``[window[0], window[1]]`` by shifting and scaling.
|
Chris@87
|
46 The default value is $domain.
|
Chris@87
|
47 window : (2,) array_like, optional
|
Chris@87
|
48 Window, see ``domain`` for its use. The default value is $domain.
|
Chris@87
|
49 .. versionadded:: 1.6.0
|
Chris@87
|
50
|
Chris@87
|
51 Attributes
|
Chris@87
|
52 ----------
|
Chris@87
|
53 coef : (N,) ndarray
|
Chris@87
|
54 $name coefficients, from low to high.
|
Chris@87
|
55 domain : (2,) ndarray
|
Chris@87
|
56 Domain that is mapped to ``window``.
|
Chris@87
|
57 window : (2,) ndarray
|
Chris@87
|
58 Window that ``domain`` is mapped to.
|
Chris@87
|
59
|
Chris@87
|
60 Class Attributes
|
Chris@87
|
61 ----------------
|
Chris@87
|
62 maxpower : int
|
Chris@87
|
63 Maximum power allowed, i.e., the largest number ``n`` such that
|
Chris@87
|
64 ``p(x)**n`` is allowed. This is to limit runaway polynomial size.
|
Chris@87
|
65 domain : (2,) ndarray
|
Chris@87
|
66 Default domain of the class.
|
Chris@87
|
67 window : (2,) ndarray
|
Chris@87
|
68 Default window of the class.
|
Chris@87
|
69
|
Chris@87
|
70 Notes
|
Chris@87
|
71 -----
|
Chris@87
|
72 It is important to specify the domain in many cases, for instance in
|
Chris@87
|
73 fitting data, because many of the important properties of the
|
Chris@87
|
74 polynomial basis only hold in a specified interval and consequently
|
Chris@87
|
75 the data must be mapped into that interval in order to benefit.
|
Chris@87
|
76
|
Chris@87
|
77 Examples
|
Chris@87
|
78 --------
|
Chris@87
|
79
|
Chris@87
|
80 """
|
Chris@87
|
81 # Limit runaway size. T_n^m has degree n*2^m
|
Chris@87
|
82 maxpower = 16
|
Chris@87
|
83 # Default domain
|
Chris@87
|
84 domain = np.array($domain)
|
Chris@87
|
85 # Default window
|
Chris@87
|
86 window = np.array($domain)
|
Chris@87
|
87 # Don't let participate in array operations. Value doesn't matter.
|
Chris@87
|
88 __array_priority__ = 1000
|
Chris@87
|
89 # Not hashable
|
Chris@87
|
90 __hash__ = None
|
Chris@87
|
91
|
Chris@87
|
92 def has_samecoef(self, other):
|
Chris@87
|
93 """Check if coefficients match.
|
Chris@87
|
94
|
Chris@87
|
95 Parameters
|
Chris@87
|
96 ----------
|
Chris@87
|
97 other : class instance
|
Chris@87
|
98 The other class must have the ``coef`` attribute.
|
Chris@87
|
99
|
Chris@87
|
100 Returns
|
Chris@87
|
101 -------
|
Chris@87
|
102 bool : boolean
|
Chris@87
|
103 True if the coefficients are the same, False otherwise.
|
Chris@87
|
104
|
Chris@87
|
105 Notes
|
Chris@87
|
106 -----
|
Chris@87
|
107 .. versionadded:: 1.6.0
|
Chris@87
|
108
|
Chris@87
|
109 """
|
Chris@87
|
110 if len(self.coef) != len(other.coef):
|
Chris@87
|
111 return False
|
Chris@87
|
112 elif not np.all(self.coef == other.coef):
|
Chris@87
|
113 return False
|
Chris@87
|
114 else:
|
Chris@87
|
115 return True
|
Chris@87
|
116
|
Chris@87
|
117 def has_samedomain(self, other):
|
Chris@87
|
118 """Check if domains match.
|
Chris@87
|
119
|
Chris@87
|
120 Parameters
|
Chris@87
|
121 ----------
|
Chris@87
|
122 other : class instance
|
Chris@87
|
123 The other class must have the ``domain`` attribute.
|
Chris@87
|
124
|
Chris@87
|
125 Returns
|
Chris@87
|
126 -------
|
Chris@87
|
127 bool : boolean
|
Chris@87
|
128 True if the domains are the same, False otherwise.
|
Chris@87
|
129
|
Chris@87
|
130 Notes
|
Chris@87
|
131 -----
|
Chris@87
|
132 .. versionadded:: 1.6.0
|
Chris@87
|
133
|
Chris@87
|
134 """
|
Chris@87
|
135 return np.all(self.domain == other.domain)
|
Chris@87
|
136
|
Chris@87
|
137 def has_samewindow(self, other):
|
Chris@87
|
138 """Check if windows match.
|
Chris@87
|
139
|
Chris@87
|
140 Parameters
|
Chris@87
|
141 ----------
|
Chris@87
|
142 other : class instance
|
Chris@87
|
143 The other class must have the ``window`` attribute.
|
Chris@87
|
144
|
Chris@87
|
145 Returns
|
Chris@87
|
146 -------
|
Chris@87
|
147 bool : boolean
|
Chris@87
|
148 True if the windows are the same, False otherwise.
|
Chris@87
|
149
|
Chris@87
|
150 Notes
|
Chris@87
|
151 -----
|
Chris@87
|
152 .. versionadded:: 1.6.0
|
Chris@87
|
153
|
Chris@87
|
154 """
|
Chris@87
|
155 return np.all(self.window == other.window)
|
Chris@87
|
156
|
Chris@87
|
157 def has_sametype(self, other):
|
Chris@87
|
158 """Check if types match.
|
Chris@87
|
159
|
Chris@87
|
160 Parameters
|
Chris@87
|
161 ----------
|
Chris@87
|
162 other : object
|
Chris@87
|
163 Class instance.
|
Chris@87
|
164
|
Chris@87
|
165 Returns
|
Chris@87
|
166 -------
|
Chris@87
|
167 bool : boolean
|
Chris@87
|
168 True if other is same class as self
|
Chris@87
|
169
|
Chris@87
|
170 Notes
|
Chris@87
|
171 -----
|
Chris@87
|
172 .. versionadded:: 1.7.0
|
Chris@87
|
173
|
Chris@87
|
174 """
|
Chris@87
|
175 return isinstance(other, self.__class__)
|
Chris@87
|
176
|
Chris@87
|
177 def __init__(self, coef, domain=$domain, window=$domain) :
|
Chris@87
|
178 [coef, dom, win] = pu.as_series([coef, domain, window], trim=False)
|
Chris@87
|
179 if len(dom) != 2 :
|
Chris@87
|
180 raise ValueError("Domain has wrong number of elements.")
|
Chris@87
|
181 if len(win) != 2 :
|
Chris@87
|
182 raise ValueError("Window has wrong number of elements.")
|
Chris@87
|
183 self.coef = coef
|
Chris@87
|
184 self.domain = dom
|
Chris@87
|
185 self.window = win
|
Chris@87
|
186
|
Chris@87
|
187 def __repr__(self):
|
Chris@87
|
188 format = "%s(%s, %s, %s)"
|
Chris@87
|
189 coef = repr(self.coef)[6:-1]
|
Chris@87
|
190 domain = repr(self.domain)[6:-1]
|
Chris@87
|
191 window = repr(self.window)[6:-1]
|
Chris@87
|
192 return format % ('$name', coef, domain, window)
|
Chris@87
|
193
|
Chris@87
|
194 def __str__(self) :
|
Chris@87
|
195 format = "%s(%s)"
|
Chris@87
|
196 coef = str(self.coef)
|
Chris@87
|
197 return format % ('$nick', coef)
|
Chris@87
|
198
|
Chris@87
|
199 # Pickle and copy
|
Chris@87
|
200
|
Chris@87
|
201 def __getstate__(self) :
|
Chris@87
|
202 ret = self.__dict__.copy()
|
Chris@87
|
203 ret['coef'] = self.coef.copy()
|
Chris@87
|
204 ret['domain'] = self.domain.copy()
|
Chris@87
|
205 ret['window'] = self.window.copy()
|
Chris@87
|
206 return ret
|
Chris@87
|
207
|
Chris@87
|
208 def __setstate__(self, dict) :
|
Chris@87
|
209 self.__dict__ = dict
|
Chris@87
|
210
|
Chris@87
|
211 # Call
|
Chris@87
|
212
|
Chris@87
|
213 def __call__(self, arg) :
|
Chris@87
|
214 off, scl = pu.mapparms(self.domain, self.window)
|
Chris@87
|
215 arg = off + scl*arg
|
Chris@87
|
216 return ${nick}val(arg, self.coef)
|
Chris@87
|
217
|
Chris@87
|
218 def __iter__(self) :
|
Chris@87
|
219 return iter(self.coef)
|
Chris@87
|
220
|
Chris@87
|
221 def __len__(self) :
|
Chris@87
|
222 return len(self.coef)
|
Chris@87
|
223
|
Chris@87
|
224 # Numeric properties.
|
Chris@87
|
225
|
Chris@87
|
226 def __neg__(self) :
|
Chris@87
|
227 return self.__class__(-self.coef, self.domain, self.window)
|
Chris@87
|
228
|
Chris@87
|
229 def __pos__(self) :
|
Chris@87
|
230 return self
|
Chris@87
|
231
|
Chris@87
|
232 def __add__(self, other) :
|
Chris@87
|
233 """Returns sum"""
|
Chris@87
|
234 if isinstance(other, pu.PolyBase):
|
Chris@87
|
235 if not self.has_sametype(other):
|
Chris@87
|
236 raise TypeError("Polynomial types differ")
|
Chris@87
|
237 elif not self.has_samedomain(other):
|
Chris@87
|
238 raise TypeError("Domains differ")
|
Chris@87
|
239 elif not self.has_samewindow(other):
|
Chris@87
|
240 raise TypeError("Windows differ")
|
Chris@87
|
241 else:
|
Chris@87
|
242 coef = ${nick}add(self.coef, other.coef)
|
Chris@87
|
243 else :
|
Chris@87
|
244 try :
|
Chris@87
|
245 coef = ${nick}add(self.coef, other)
|
Chris@87
|
246 except :
|
Chris@87
|
247 return NotImplemented
|
Chris@87
|
248 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
249
|
Chris@87
|
250 def __sub__(self, other) :
|
Chris@87
|
251 """Returns difference"""
|
Chris@87
|
252 if isinstance(other, pu.PolyBase):
|
Chris@87
|
253 if not self.has_sametype(other):
|
Chris@87
|
254 raise TypeError("Polynomial types differ")
|
Chris@87
|
255 elif not self.has_samedomain(other):
|
Chris@87
|
256 raise TypeError("Domains differ")
|
Chris@87
|
257 elif not self.has_samewindow(other):
|
Chris@87
|
258 raise TypeError("Windows differ")
|
Chris@87
|
259 else:
|
Chris@87
|
260 coef = ${nick}sub(self.coef, other.coef)
|
Chris@87
|
261 else :
|
Chris@87
|
262 try :
|
Chris@87
|
263 coef = ${nick}sub(self.coef, other)
|
Chris@87
|
264 except :
|
Chris@87
|
265 return NotImplemented
|
Chris@87
|
266 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
267
|
Chris@87
|
268 def __mul__(self, other) :
|
Chris@87
|
269 """Returns product"""
|
Chris@87
|
270 if isinstance(other, pu.PolyBase):
|
Chris@87
|
271 if not self.has_sametype(other):
|
Chris@87
|
272 raise TypeError("Polynomial types differ")
|
Chris@87
|
273 elif not self.has_samedomain(other):
|
Chris@87
|
274 raise TypeError("Domains differ")
|
Chris@87
|
275 elif not self.has_samewindow(other):
|
Chris@87
|
276 raise TypeError("Windows differ")
|
Chris@87
|
277 else:
|
Chris@87
|
278 coef = ${nick}mul(self.coef, other.coef)
|
Chris@87
|
279 else :
|
Chris@87
|
280 try :
|
Chris@87
|
281 coef = ${nick}mul(self.coef, other)
|
Chris@87
|
282 except :
|
Chris@87
|
283 return NotImplemented
|
Chris@87
|
284 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
285
|
Chris@87
|
286 def __div__(self, other):
|
Chris@87
|
287 # set to __floordiv__, /, for now.
|
Chris@87
|
288 return self.__floordiv__(other)
|
Chris@87
|
289
|
Chris@87
|
290 def __truediv__(self, other) :
|
Chris@87
|
291 # there is no true divide if the rhs is not a Number, although it
|
Chris@87
|
292 # could return the first n elements of an infinite series.
|
Chris@87
|
293 # It is hard to see where n would come from, though.
|
Chris@87
|
294 if not isinstance(other, Number) or isinstance(other, bool):
|
Chris@87
|
295 form = "unsupported types for true division: '%s', '%s'"
|
Chris@87
|
296 raise TypeError(form % (type(self), type(other)))
|
Chris@87
|
297 return self.__floordiv__(other)
|
Chris@87
|
298
|
Chris@87
|
299 def __floordiv__(self, other) :
|
Chris@87
|
300 """Returns the quotient."""
|
Chris@87
|
301 if isinstance(other, pu.PolyBase):
|
Chris@87
|
302 if not self.has_sametype(other):
|
Chris@87
|
303 raise TypeError("Polynomial types differ")
|
Chris@87
|
304 elif not self.has_samedomain(other):
|
Chris@87
|
305 raise TypeError("Domains differ")
|
Chris@87
|
306 elif not self.has_samewindow(other):
|
Chris@87
|
307 raise TypeError("Windows differ")
|
Chris@87
|
308 else:
|
Chris@87
|
309 quo, rem = ${nick}div(self.coef, other.coef)
|
Chris@87
|
310 else :
|
Chris@87
|
311 try :
|
Chris@87
|
312 quo, rem = ${nick}div(self.coef, other)
|
Chris@87
|
313 except :
|
Chris@87
|
314 return NotImplemented
|
Chris@87
|
315 return self.__class__(quo, self.domain, self.window)
|
Chris@87
|
316
|
Chris@87
|
317 def __mod__(self, other) :
|
Chris@87
|
318 """Returns the remainder."""
|
Chris@87
|
319 if isinstance(other, pu.PolyBase):
|
Chris@87
|
320 if not self.has_sametype(other):
|
Chris@87
|
321 raise TypeError("Polynomial types differ")
|
Chris@87
|
322 elif not self.has_samedomain(other):
|
Chris@87
|
323 raise TypeError("Domains differ")
|
Chris@87
|
324 elif not self.has_samewindow(other):
|
Chris@87
|
325 raise TypeError("Windows differ")
|
Chris@87
|
326 else:
|
Chris@87
|
327 quo, rem = ${nick}div(self.coef, other.coef)
|
Chris@87
|
328 else :
|
Chris@87
|
329 try :
|
Chris@87
|
330 quo, rem = ${nick}div(self.coef, other)
|
Chris@87
|
331 except :
|
Chris@87
|
332 return NotImplemented
|
Chris@87
|
333 return self.__class__(rem, self.domain, self.window)
|
Chris@87
|
334
|
Chris@87
|
335 def __divmod__(self, other) :
|
Chris@87
|
336 """Returns quo, remainder"""
|
Chris@87
|
337 if isinstance(other, self.__class__) :
|
Chris@87
|
338 if not self.has_samedomain(other):
|
Chris@87
|
339 raise TypeError("Domains are not equal")
|
Chris@87
|
340 elif not self.has_samewindow(other):
|
Chris@87
|
341 raise TypeError("Windows are not equal")
|
Chris@87
|
342 else:
|
Chris@87
|
343 quo, rem = ${nick}div(self.coef, other.coef)
|
Chris@87
|
344 else :
|
Chris@87
|
345 try :
|
Chris@87
|
346 quo, rem = ${nick}div(self.coef, other)
|
Chris@87
|
347 except :
|
Chris@87
|
348 return NotImplemented
|
Chris@87
|
349 quo = self.__class__(quo, self.domain, self.window)
|
Chris@87
|
350 rem = self.__class__(rem, self.domain, self.window)
|
Chris@87
|
351 return quo, rem
|
Chris@87
|
352
|
Chris@87
|
353 def __pow__(self, other) :
|
Chris@87
|
354 try :
|
Chris@87
|
355 coef = ${nick}pow(self.coef, other, maxpower = self.maxpower)
|
Chris@87
|
356 except :
|
Chris@87
|
357 raise
|
Chris@87
|
358 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
359
|
Chris@87
|
360 def __radd__(self, other) :
|
Chris@87
|
361 try :
|
Chris@87
|
362 coef = ${nick}add(other, self.coef)
|
Chris@87
|
363 except :
|
Chris@87
|
364 return NotImplemented
|
Chris@87
|
365 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
366
|
Chris@87
|
367 def __rsub__(self, other):
|
Chris@87
|
368 try :
|
Chris@87
|
369 coef = ${nick}sub(other, self.coef)
|
Chris@87
|
370 except :
|
Chris@87
|
371 return NotImplemented
|
Chris@87
|
372 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
373
|
Chris@87
|
374 def __rmul__(self, other) :
|
Chris@87
|
375 try :
|
Chris@87
|
376 coef = ${nick}mul(other, self.coef)
|
Chris@87
|
377 except :
|
Chris@87
|
378 return NotImplemented
|
Chris@87
|
379 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
380
|
Chris@87
|
381 def __rdiv__(self, other):
|
Chris@87
|
382 # set to __floordiv__ /.
|
Chris@87
|
383 return self.__rfloordiv__(other)
|
Chris@87
|
384
|
Chris@87
|
385 def __rtruediv__(self, other) :
|
Chris@87
|
386 # An instance of PolyBase is not considered a
|
Chris@87
|
387 # Number.
|
Chris@87
|
388 return NotImplemented
|
Chris@87
|
389
|
Chris@87
|
390 def __rfloordiv__(self, other) :
|
Chris@87
|
391 try :
|
Chris@87
|
392 quo, rem = ${nick}div(other, self.coef)
|
Chris@87
|
393 except:
|
Chris@87
|
394 return NotImplemented
|
Chris@87
|
395 return self.__class__(quo, self.domain, self.window)
|
Chris@87
|
396
|
Chris@87
|
397 def __rmod__(self, other) :
|
Chris@87
|
398 try :
|
Chris@87
|
399 quo, rem = ${nick}div(other, self.coef)
|
Chris@87
|
400 except :
|
Chris@87
|
401 return NotImplemented
|
Chris@87
|
402 return self.__class__(rem, self.domain, self.window)
|
Chris@87
|
403
|
Chris@87
|
404 def __rdivmod__(self, other) :
|
Chris@87
|
405 try :
|
Chris@87
|
406 quo, rem = ${nick}div(other, self.coef)
|
Chris@87
|
407 except :
|
Chris@87
|
408 return NotImplemented
|
Chris@87
|
409 quo = self.__class__(quo, self.domain, self.window)
|
Chris@87
|
410 rem = self.__class__(rem, self.domain, self.window)
|
Chris@87
|
411 return quo, rem
|
Chris@87
|
412
|
Chris@87
|
413 # Enhance me
|
Chris@87
|
414 # some augmented arithmetic operations could be added here
|
Chris@87
|
415
|
Chris@87
|
416 def __eq__(self, other) :
|
Chris@87
|
417 res = isinstance(other, self.__class__) \
|
Chris@87
|
418 and self.has_samecoef(other) \
|
Chris@87
|
419 and self.has_samedomain(other) \
|
Chris@87
|
420 and self.has_samewindow(other)
|
Chris@87
|
421 return res
|
Chris@87
|
422
|
Chris@87
|
423 def __ne__(self, other) :
|
Chris@87
|
424 return not self.__eq__(other)
|
Chris@87
|
425
|
Chris@87
|
426 #
|
Chris@87
|
427 # Extra methods.
|
Chris@87
|
428 #
|
Chris@87
|
429
|
Chris@87
|
430 def copy(self) :
|
Chris@87
|
431 """Return a copy.
|
Chris@87
|
432
|
Chris@87
|
433 Return a copy of the current $name instance.
|
Chris@87
|
434
|
Chris@87
|
435 Returns
|
Chris@87
|
436 -------
|
Chris@87
|
437 new_instance : $name
|
Chris@87
|
438 Copy of current instance.
|
Chris@87
|
439
|
Chris@87
|
440 """
|
Chris@87
|
441 return self.__class__(self.coef, self.domain, self.window)
|
Chris@87
|
442
|
Chris@87
|
443 def degree(self) :
|
Chris@87
|
444 """The degree of the series.
|
Chris@87
|
445
|
Chris@87
|
446 Notes
|
Chris@87
|
447 -----
|
Chris@87
|
448 .. versionadded:: 1.5.0
|
Chris@87
|
449
|
Chris@87
|
450 """
|
Chris@87
|
451 return len(self) - 1
|
Chris@87
|
452
|
Chris@87
|
453 def cutdeg(self, deg) :
|
Chris@87
|
454 """Truncate series to the given degree.
|
Chris@87
|
455
|
Chris@87
|
456 Reduce the degree of the $name series to `deg` by discarding the
|
Chris@87
|
457 high order terms. If `deg` is greater than the current degree a
|
Chris@87
|
458 copy of the current series is returned. This can be useful in least
|
Chris@87
|
459 squares where the coefficients of the high degree terms may be very
|
Chris@87
|
460 small.
|
Chris@87
|
461
|
Chris@87
|
462 Parameters
|
Chris@87
|
463 ----------
|
Chris@87
|
464 deg : non-negative int
|
Chris@87
|
465 The series is reduced to degree `deg` by discarding the high
|
Chris@87
|
466 order terms. The value of `deg` must be a non-negative integer.
|
Chris@87
|
467
|
Chris@87
|
468 Returns
|
Chris@87
|
469 -------
|
Chris@87
|
470 new_instance : $name
|
Chris@87
|
471 New instance of $name with reduced degree.
|
Chris@87
|
472
|
Chris@87
|
473 Notes
|
Chris@87
|
474 -----
|
Chris@87
|
475 .. versionadded:: 1.5.0
|
Chris@87
|
476
|
Chris@87
|
477 """
|
Chris@87
|
478 return self.truncate(deg + 1)
|
Chris@87
|
479
|
Chris@87
|
480 def trim(self, tol=0) :
|
Chris@87
|
481 """Remove small leading coefficients
|
Chris@87
|
482
|
Chris@87
|
483 Remove leading coefficients until a coefficient is reached whose
|
Chris@87
|
484 absolute value greater than `tol` or the beginning of the series is
|
Chris@87
|
485 reached. If all the coefficients would be removed the series is set to
|
Chris@87
|
486 ``[0]``. A new $name instance is returned with the new coefficients.
|
Chris@87
|
487 The current instance remains unchanged.
|
Chris@87
|
488
|
Chris@87
|
489 Parameters
|
Chris@87
|
490 ----------
|
Chris@87
|
491 tol : non-negative number.
|
Chris@87
|
492 All trailing coefficients less than `tol` will be removed.
|
Chris@87
|
493
|
Chris@87
|
494 Returns
|
Chris@87
|
495 -------
|
Chris@87
|
496 new_instance : $name
|
Chris@87
|
497 Contains the new set of coefficients.
|
Chris@87
|
498
|
Chris@87
|
499 """
|
Chris@87
|
500 coef = pu.trimcoef(self.coef, tol)
|
Chris@87
|
501 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
502
|
Chris@87
|
503 def truncate(self, size) :
|
Chris@87
|
504 """Truncate series to length `size`.
|
Chris@87
|
505
|
Chris@87
|
506 Reduce the $name series to length `size` by discarding the high
|
Chris@87
|
507 degree terms. The value of `size` must be a positive integer. This
|
Chris@87
|
508 can be useful in least squares where the coefficients of the
|
Chris@87
|
509 high degree terms may be very small.
|
Chris@87
|
510
|
Chris@87
|
511 Parameters
|
Chris@87
|
512 ----------
|
Chris@87
|
513 size : positive int
|
Chris@87
|
514 The series is reduced to length `size` by discarding the high
|
Chris@87
|
515 degree terms. The value of `size` must be a positive integer.
|
Chris@87
|
516
|
Chris@87
|
517 Returns
|
Chris@87
|
518 -------
|
Chris@87
|
519 new_instance : $name
|
Chris@87
|
520 New instance of $name with truncated coefficients.
|
Chris@87
|
521
|
Chris@87
|
522 """
|
Chris@87
|
523 isize = int(size)
|
Chris@87
|
524 if isize != size or isize < 1 :
|
Chris@87
|
525 raise ValueError("size must be a positive integer")
|
Chris@87
|
526 if isize >= len(self.coef) :
|
Chris@87
|
527 coef = self.coef
|
Chris@87
|
528 else :
|
Chris@87
|
529 coef = self.coef[:isize]
|
Chris@87
|
530 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
531
|
Chris@87
|
532 def convert(self, domain=None, kind=None, window=None) :
|
Chris@87
|
533 """Convert to different class and/or domain.
|
Chris@87
|
534
|
Chris@87
|
535 Parameters
|
Chris@87
|
536 ----------
|
Chris@87
|
537 domain : array_like, optional
|
Chris@87
|
538 The domain of the converted series. If the value is None,
|
Chris@87
|
539 the default domain of `kind` is used.
|
Chris@87
|
540 kind : class, optional
|
Chris@87
|
541 The polynomial series type class to which the current instance
|
Chris@87
|
542 should be converted. If kind is None, then the class of the
|
Chris@87
|
543 current instance is used.
|
Chris@87
|
544 window : array_like, optional
|
Chris@87
|
545 The window of the converted series. If the value is None,
|
Chris@87
|
546 the default window of `kind` is used.
|
Chris@87
|
547
|
Chris@87
|
548 Returns
|
Chris@87
|
549 -------
|
Chris@87
|
550 new_series_instance : `kind`
|
Chris@87
|
551 The returned class can be of different type than the current
|
Chris@87
|
552 instance and/or have a different domain.
|
Chris@87
|
553
|
Chris@87
|
554 Notes
|
Chris@87
|
555 -----
|
Chris@87
|
556 Conversion between domains and class types can result in
|
Chris@87
|
557 numerically ill defined series.
|
Chris@87
|
558
|
Chris@87
|
559 Examples
|
Chris@87
|
560 --------
|
Chris@87
|
561
|
Chris@87
|
562 """
|
Chris@87
|
563 if kind is None:
|
Chris@87
|
564 kind = $name
|
Chris@87
|
565 if domain is None:
|
Chris@87
|
566 domain = kind.domain
|
Chris@87
|
567 if window is None:
|
Chris@87
|
568 window = kind.window
|
Chris@87
|
569 return self(kind.identity(domain, window=window))
|
Chris@87
|
570
|
Chris@87
|
571 def mapparms(self) :
|
Chris@87
|
572 """Return the mapping parameters.
|
Chris@87
|
573
|
Chris@87
|
574 The returned values define a linear map ``off + scl*x`` that is
|
Chris@87
|
575 applied to the input arguments before the series is evaluated. The
|
Chris@87
|
576 map depends on the ``domain`` and ``window``; if the current
|
Chris@87
|
577 ``domain`` is equal to the ``window`` the resulting map is the
|
Chris@87
|
578 identity. If the coefficients of the ``$name`` instance are to be
|
Chris@87
|
579 used by themselves outside this class, then the linear function
|
Chris@87
|
580 must be substituted for the ``x`` in the standard representation of
|
Chris@87
|
581 the base polynomials.
|
Chris@87
|
582
|
Chris@87
|
583 Returns
|
Chris@87
|
584 -------
|
Chris@87
|
585 off, scl : floats or complex
|
Chris@87
|
586 The mapping function is defined by ``off + scl*x``.
|
Chris@87
|
587
|
Chris@87
|
588 Notes
|
Chris@87
|
589 -----
|
Chris@87
|
590 If the current domain is the interval ``[l_1, r_1]`` and the window
|
Chris@87
|
591 is ``[l_2, r_2]``, then the linear mapping function ``L`` is
|
Chris@87
|
592 defined by the equations::
|
Chris@87
|
593
|
Chris@87
|
594 L(l_1) = l_2
|
Chris@87
|
595 L(r_1) = r_2
|
Chris@87
|
596
|
Chris@87
|
597 """
|
Chris@87
|
598 return pu.mapparms(self.domain, self.window)
|
Chris@87
|
599
|
Chris@87
|
600 def integ(self, m=1, k=[], lbnd=None) :
|
Chris@87
|
601 """Integrate.
|
Chris@87
|
602
|
Chris@87
|
603 Return an instance of $name that is the definite integral of the
|
Chris@87
|
604 current series. Refer to `${nick}int` for full documentation.
|
Chris@87
|
605
|
Chris@87
|
606 Parameters
|
Chris@87
|
607 ----------
|
Chris@87
|
608 m : non-negative int
|
Chris@87
|
609 The number of integrations to perform.
|
Chris@87
|
610 k : array_like
|
Chris@87
|
611 Integration constants. The first constant is applied to the
|
Chris@87
|
612 first integration, the second to the second, and so on. The
|
Chris@87
|
613 list of values must less than or equal to `m` in length and any
|
Chris@87
|
614 missing values are set to zero.
|
Chris@87
|
615 lbnd : Scalar
|
Chris@87
|
616 The lower bound of the definite integral.
|
Chris@87
|
617
|
Chris@87
|
618 Returns
|
Chris@87
|
619 -------
|
Chris@87
|
620 integral : $name
|
Chris@87
|
621 The integral of the series using the same domain.
|
Chris@87
|
622
|
Chris@87
|
623 See Also
|
Chris@87
|
624 --------
|
Chris@87
|
625 ${nick}int : similar function.
|
Chris@87
|
626 ${nick}der : similar function for derivative.
|
Chris@87
|
627
|
Chris@87
|
628 """
|
Chris@87
|
629 off, scl = self.mapparms()
|
Chris@87
|
630 if lbnd is None :
|
Chris@87
|
631 lbnd = 0
|
Chris@87
|
632 else :
|
Chris@87
|
633 lbnd = off + scl*lbnd
|
Chris@87
|
634 coef = ${nick}int(self.coef, m, k, lbnd, 1./scl)
|
Chris@87
|
635 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
636
|
Chris@87
|
637 def deriv(self, m=1):
|
Chris@87
|
638 """Differentiate.
|
Chris@87
|
639
|
Chris@87
|
640 Return an instance of $name that is the derivative of the current
|
Chris@87
|
641 series. Refer to `${nick}der` for full documentation.
|
Chris@87
|
642
|
Chris@87
|
643 Parameters
|
Chris@87
|
644 ----------
|
Chris@87
|
645 m : non-negative int
|
Chris@87
|
646 The number of integrations to perform.
|
Chris@87
|
647
|
Chris@87
|
648 Returns
|
Chris@87
|
649 -------
|
Chris@87
|
650 derivative : $name
|
Chris@87
|
651 The derivative of the series using the same domain.
|
Chris@87
|
652
|
Chris@87
|
653 See Also
|
Chris@87
|
654 --------
|
Chris@87
|
655 ${nick}der : similar function.
|
Chris@87
|
656 ${nick}int : similar function for integration.
|
Chris@87
|
657
|
Chris@87
|
658 """
|
Chris@87
|
659 off, scl = self.mapparms()
|
Chris@87
|
660 coef = ${nick}der(self.coef, m, scl)
|
Chris@87
|
661 return self.__class__(coef, self.domain, self.window)
|
Chris@87
|
662
|
Chris@87
|
663 def roots(self) :
|
Chris@87
|
664 """Return list of roots.
|
Chris@87
|
665
|
Chris@87
|
666 Return ndarray of roots for this series. See `${nick}roots` for
|
Chris@87
|
667 full documentation. Note that the accuracy of the roots is likely to
|
Chris@87
|
668 decrease the further outside the domain they lie.
|
Chris@87
|
669
|
Chris@87
|
670 See Also
|
Chris@87
|
671 --------
|
Chris@87
|
672 ${nick}roots : similar function
|
Chris@87
|
673 ${nick}fromroots : function to go generate series from roots.
|
Chris@87
|
674
|
Chris@87
|
675 """
|
Chris@87
|
676 roots = ${nick}roots(self.coef)
|
Chris@87
|
677 return pu.mapdomain(roots, self.window, self.domain)
|
Chris@87
|
678
|
Chris@87
|
679 def linspace(self, n=100, domain=None):
|
Chris@87
|
680 """Return x,y values at equally spaced points in domain.
|
Chris@87
|
681
|
Chris@87
|
682 Returns x, y values at `n` linearly spaced points across domain.
|
Chris@87
|
683 Here y is the value of the polynomial at the points x. By default
|
Chris@87
|
684 the domain is the same as that of the $name instance. This method
|
Chris@87
|
685 is intended mostly as a plotting aid.
|
Chris@87
|
686
|
Chris@87
|
687 Parameters
|
Chris@87
|
688 ----------
|
Chris@87
|
689 n : int, optional
|
Chris@87
|
690 Number of point pairs to return. The default value is 100.
|
Chris@87
|
691 domain : {None, array_like}
|
Chris@87
|
692 If not None, the specified domain is used instead of that of
|
Chris@87
|
693 the calling instance. It should be of the form ``[beg,end]``.
|
Chris@87
|
694 The default is None.
|
Chris@87
|
695
|
Chris@87
|
696 Returns
|
Chris@87
|
697 -------
|
Chris@87
|
698 x, y : ndarrays
|
Chris@87
|
699 ``x`` is equal to linspace(self.domain[0], self.domain[1], n)
|
Chris@87
|
700 ``y`` is the polynomial evaluated at ``x``.
|
Chris@87
|
701
|
Chris@87
|
702 .. versionadded:: 1.5.0
|
Chris@87
|
703
|
Chris@87
|
704 """
|
Chris@87
|
705 if domain is None:
|
Chris@87
|
706 domain = self.domain
|
Chris@87
|
707 x = np.linspace(domain[0], domain[1], n)
|
Chris@87
|
708 y = self(x)
|
Chris@87
|
709 return x, y
|
Chris@87
|
710
|
Chris@87
|
711
|
Chris@87
|
712
|
Chris@87
|
713 @staticmethod
|
Chris@87
|
714 def fit(x, y, deg, domain=None, rcond=None, full=False, w=None,
|
Chris@87
|
715 window=$domain):
|
Chris@87
|
716 """Least squares fit to data.
|
Chris@87
|
717
|
Chris@87
|
718 Return a `$name` instance that is the least squares fit to the data
|
Chris@87
|
719 `y` sampled at `x`. Unlike `${nick}fit`, the domain of the returned
|
Chris@87
|
720 instance can be specified and this will often result in a superior
|
Chris@87
|
721 fit with less chance of ill conditioning. Support for NA was added
|
Chris@87
|
722 in version 1.7.0. See `${nick}fit` for full documentation of the
|
Chris@87
|
723 implementation.
|
Chris@87
|
724
|
Chris@87
|
725 Parameters
|
Chris@87
|
726 ----------
|
Chris@87
|
727 x : array_like, shape (M,)
|
Chris@87
|
728 x-coordinates of the M sample points ``(x[i], y[i])``.
|
Chris@87
|
729 y : array_like, shape (M,) or (M, K)
|
Chris@87
|
730 y-coordinates of the sample points. Several data sets of sample
|
Chris@87
|
731 points sharing the same x-coordinates can be fitted at once by
|
Chris@87
|
732 passing in a 2D-array that contains one dataset per column.
|
Chris@87
|
733 deg : int
|
Chris@87
|
734 Degree of the fitting polynomial.
|
Chris@87
|
735 domain : {None, [beg, end], []}, optional
|
Chris@87
|
736 Domain to use for the returned $name instance. If ``None``,
|
Chris@87
|
737 then a minimal domain that covers the points `x` is chosen. If
|
Chris@87
|
738 ``[]`` the default domain ``$domain`` is used. The default
|
Chris@87
|
739 value is $domain in numpy 1.4.x and ``None`` in later versions.
|
Chris@87
|
740 The ``[]`` value was added in numpy 1.5.0.
|
Chris@87
|
741 rcond : float, optional
|
Chris@87
|
742 Relative condition number of the fit. Singular values smaller
|
Chris@87
|
743 than this relative to the largest singular value will be
|
Chris@87
|
744 ignored. The default value is len(x)*eps, where eps is the
|
Chris@87
|
745 relative precision of the float type, about 2e-16 in most
|
Chris@87
|
746 cases.
|
Chris@87
|
747 full : bool, optional
|
Chris@87
|
748 Switch determining nature of return value. When it is False
|
Chris@87
|
749 (the default) just the coefficients are returned, when True
|
Chris@87
|
750 diagnostic information from the singular value decomposition is
|
Chris@87
|
751 also returned.
|
Chris@87
|
752 w : array_like, shape (M,), optional
|
Chris@87
|
753 Weights. If not None the contribution of each point
|
Chris@87
|
754 ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the
|
Chris@87
|
755 weights are chosen so that the errors of the products
|
Chris@87
|
756 ``w[i]*y[i]`` all have the same variance. The default value is
|
Chris@87
|
757 None.
|
Chris@87
|
758 .. versionadded:: 1.5.0
|
Chris@87
|
759 window : {[beg, end]}, optional
|
Chris@87
|
760 Window to use for the returned $name instance. The default
|
Chris@87
|
761 value is ``$domain``
|
Chris@87
|
762 .. versionadded:: 1.6.0
|
Chris@87
|
763
|
Chris@87
|
764 Returns
|
Chris@87
|
765 -------
|
Chris@87
|
766 least_squares_fit : instance of $name
|
Chris@87
|
767 The $name instance is the least squares fit to the data and
|
Chris@87
|
768 has the domain specified in the call.
|
Chris@87
|
769
|
Chris@87
|
770 [residuals, rank, singular_values, rcond] : only if `full` = True
|
Chris@87
|
771 Residuals of the least squares fit, the effective rank of the
|
Chris@87
|
772 scaled Vandermonde matrix and its singular values, and the
|
Chris@87
|
773 specified value of `rcond`. For more details, see
|
Chris@87
|
774 `linalg.lstsq`.
|
Chris@87
|
775
|
Chris@87
|
776 See Also
|
Chris@87
|
777 --------
|
Chris@87
|
778 ${nick}fit : similar function
|
Chris@87
|
779
|
Chris@87
|
780 """
|
Chris@87
|
781 if domain is None:
|
Chris@87
|
782 domain = pu.getdomain(x)
|
Chris@87
|
783 elif type(domain) is list and len(domain) == 0:
|
Chris@87
|
784 domain = $domain
|
Chris@87
|
785
|
Chris@87
|
786 if type(window) is list and len(window) == 0:
|
Chris@87
|
787 window = $domain
|
Chris@87
|
788
|
Chris@87
|
789 xnew = pu.mapdomain(x, domain, window)
|
Chris@87
|
790 res = ${nick}fit(xnew, y, deg, w=w, rcond=rcond, full=full)
|
Chris@87
|
791 if full :
|
Chris@87
|
792 [coef, status] = res
|
Chris@87
|
793 return $name(coef, domain=domain, window=window), status
|
Chris@87
|
794 else :
|
Chris@87
|
795 coef = res
|
Chris@87
|
796 return $name(coef, domain=domain, window=window)
|
Chris@87
|
797
|
Chris@87
|
798 @staticmethod
|
Chris@87
|
799 def fromroots(roots, domain=$domain, window=$domain) :
|
Chris@87
|
800 """Return $name instance with specified roots.
|
Chris@87
|
801
|
Chris@87
|
802 Returns an instance of $name representing the product
|
Chris@87
|
803 ``(x - r[0])*(x - r[1])*...*(x - r[n-1])``, where ``r`` is the
|
Chris@87
|
804 list of roots.
|
Chris@87
|
805
|
Chris@87
|
806 Parameters
|
Chris@87
|
807 ----------
|
Chris@87
|
808 roots : array_like
|
Chris@87
|
809 List of roots.
|
Chris@87
|
810 domain : {array_like, None}, optional
|
Chris@87
|
811 Domain for the resulting instance of $name. If none the domain
|
Chris@87
|
812 is the interval from the smallest root to the largest. The
|
Chris@87
|
813 default is $domain.
|
Chris@87
|
814 window : array_like, optional
|
Chris@87
|
815 Window for the resulting instance of $name. The default value
|
Chris@87
|
816 is $domain.
|
Chris@87
|
817
|
Chris@87
|
818 Returns
|
Chris@87
|
819 -------
|
Chris@87
|
820 object : $name instance
|
Chris@87
|
821 Series with the specified roots.
|
Chris@87
|
822
|
Chris@87
|
823 See Also
|
Chris@87
|
824 --------
|
Chris@87
|
825 ${nick}fromroots : equivalent function
|
Chris@87
|
826
|
Chris@87
|
827 """
|
Chris@87
|
828 [roots] = pu.as_series([roots], trim=False)
|
Chris@87
|
829 if domain is None :
|
Chris@87
|
830 domain = pu.getdomain(roots)
|
Chris@87
|
831 deg = len(roots)
|
Chris@87
|
832 off, scl = pu.mapparms(domain, window)
|
Chris@87
|
833 rnew = off + scl*roots
|
Chris@87
|
834 coef = ${nick}fromroots(rnew) / scl**deg
|
Chris@87
|
835 return $name(coef, domain=domain, window=window)
|
Chris@87
|
836
|
Chris@87
|
837 @staticmethod
|
Chris@87
|
838 def identity(domain=$domain, window=$domain) :
|
Chris@87
|
839 """Identity function.
|
Chris@87
|
840
|
Chris@87
|
841 If ``p`` is the returned $name object, then ``p(x) == x`` for all
|
Chris@87
|
842 values of x.
|
Chris@87
|
843
|
Chris@87
|
844 Parameters
|
Chris@87
|
845 ----------
|
Chris@87
|
846 domain : array_like
|
Chris@87
|
847 The resulting array must be of the form ``[beg, end]``, where
|
Chris@87
|
848 ``beg`` and ``end`` are the endpoints of the domain.
|
Chris@87
|
849 window : array_like
|
Chris@87
|
850 The resulting array must be if the form ``[beg, end]``, where
|
Chris@87
|
851 ``beg`` and ``end`` are the endpoints of the window.
|
Chris@87
|
852
|
Chris@87
|
853 Returns
|
Chris@87
|
854 -------
|
Chris@87
|
855 identity : $name instance
|
Chris@87
|
856
|
Chris@87
|
857 """
|
Chris@87
|
858 off, scl = pu.mapparms(window, domain)
|
Chris@87
|
859 coef = ${nick}line(off, scl)
|
Chris@87
|
860 return $name(coef, domain, window)
|
Chris@87
|
861
|
Chris@87
|
862 @staticmethod
|
Chris@87
|
863 def basis(deg, domain=$domain, window=$domain):
|
Chris@87
|
864 """$name polynomial of degree `deg`.
|
Chris@87
|
865
|
Chris@87
|
866 Returns an instance of the $name polynomial of degree `d`.
|
Chris@87
|
867
|
Chris@87
|
868 Parameters
|
Chris@87
|
869 ----------
|
Chris@87
|
870 deg : int
|
Chris@87
|
871 Degree of the $name polynomial. Must be >= 0.
|
Chris@87
|
872 domain : array_like
|
Chris@87
|
873 The resulting array must be of the form ``[beg, end]``, where
|
Chris@87
|
874 ``beg`` and ``end`` are the endpoints of the domain.
|
Chris@87
|
875 window : array_like
|
Chris@87
|
876 The resulting array must be if the form ``[beg, end]``, where
|
Chris@87
|
877 ``beg`` and ``end`` are the endpoints of the window.
|
Chris@87
|
878
|
Chris@87
|
879 Returns
|
Chris@87
|
880 p : $name instance
|
Chris@87
|
881
|
Chris@87
|
882 Notes
|
Chris@87
|
883 -----
|
Chris@87
|
884 .. versionadded:: 1.7.0
|
Chris@87
|
885
|
Chris@87
|
886 """
|
Chris@87
|
887 ideg = int(deg)
|
Chris@87
|
888 if ideg != deg or ideg < 0:
|
Chris@87
|
889 raise ValueError("deg must be non-negative integer")
|
Chris@87
|
890 return $name([0]*ideg + [1], domain, window)
|
Chris@87
|
891
|
Chris@87
|
892 @staticmethod
|
Chris@87
|
893 def cast(series, domain=$domain, window=$domain):
|
Chris@87
|
894 """Convert instance to equivalent $name series.
|
Chris@87
|
895
|
Chris@87
|
896 The `series` is expected to be an instance of some polynomial
|
Chris@87
|
897 series of one of the types supported by by the numpy.polynomial
|
Chris@87
|
898 module, but could be some other class that supports the convert
|
Chris@87
|
899 method.
|
Chris@87
|
900
|
Chris@87
|
901 Parameters
|
Chris@87
|
902 ----------
|
Chris@87
|
903 series : series
|
Chris@87
|
904 The instance series to be converted.
|
Chris@87
|
905 domain : array_like
|
Chris@87
|
906 The resulting array must be of the form ``[beg, end]``, where
|
Chris@87
|
907 ``beg`` and ``end`` are the endpoints of the domain.
|
Chris@87
|
908 window : array_like
|
Chris@87
|
909 The resulting array must be if the form ``[beg, end]``, where
|
Chris@87
|
910 ``beg`` and ``end`` are the endpoints of the window.
|
Chris@87
|
911
|
Chris@87
|
912 Returns
|
Chris@87
|
913 p : $name instance
|
Chris@87
|
914 A $name series equal to the `poly` series.
|
Chris@87
|
915
|
Chris@87
|
916 See Also
|
Chris@87
|
917 --------
|
Chris@87
|
918 convert -- similar instance method
|
Chris@87
|
919
|
Chris@87
|
920 Notes
|
Chris@87
|
921 -----
|
Chris@87
|
922 .. versionadded:: 1.7.0
|
Chris@87
|
923
|
Chris@87
|
924 """
|
Chris@87
|
925 return series.convert(domain, $name, window)
|
Chris@87
|
926
|
Chris@87
|
927 ''')
|