annotate DEPENDENCIES/mingw32/Python27/Lib/site-packages/numpy/polynomial/legendre.py @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents 2a2c65a20a8b
children
rev   line source
Chris@87 1 """
Chris@87 2 Legendre Series (:mod: `numpy.polynomial.legendre`)
Chris@87 3 ===================================================
Chris@87 4
Chris@87 5 .. currentmodule:: numpy.polynomial.polynomial
Chris@87 6
Chris@87 7 This module provides a number of objects (mostly functions) useful for
Chris@87 8 dealing with Legendre series, including a `Legendre` class that
Chris@87 9 encapsulates the usual arithmetic operations. (General information
Chris@87 10 on how this module represents and works with such polynomials is in the
Chris@87 11 docstring for its "parent" sub-package, `numpy.polynomial`).
Chris@87 12
Chris@87 13 Constants
Chris@87 14 ---------
Chris@87 15
Chris@87 16 .. autosummary::
Chris@87 17 :toctree: generated/
Chris@87 18
Chris@87 19 legdomain Legendre series default domain, [-1,1].
Chris@87 20 legzero Legendre series that evaluates identically to 0.
Chris@87 21 legone Legendre series that evaluates identically to 1.
Chris@87 22 legx Legendre series for the identity map, ``f(x) = x``.
Chris@87 23
Chris@87 24 Arithmetic
Chris@87 25 ----------
Chris@87 26
Chris@87 27 .. autosummary::
Chris@87 28 :toctree: generated/
Chris@87 29
Chris@87 30 legmulx multiply a Legendre series in P_i(x) by x.
Chris@87 31 legadd add two Legendre series.
Chris@87 32 legsub subtract one Legendre series from another.
Chris@87 33 legmul multiply two Legendre series.
Chris@87 34 legdiv divide one Legendre series by another.
Chris@87 35 legpow raise a Legendre series to an positive integer power
Chris@87 36 legval evaluate a Legendre series at given points.
Chris@87 37 legval2d evaluate a 2D Legendre series at given points.
Chris@87 38 legval3d evaluate a 3D Legendre series at given points.
Chris@87 39 leggrid2d evaluate a 2D Legendre series on a Cartesian product.
Chris@87 40 leggrid3d evaluate a 3D Legendre series on a Cartesian product.
Chris@87 41
Chris@87 42 Calculus
Chris@87 43 --------
Chris@87 44
Chris@87 45 .. autosummary::
Chris@87 46 :toctree: generated/
Chris@87 47
Chris@87 48 legder differentiate a Legendre series.
Chris@87 49 legint integrate a Legendre series.
Chris@87 50
Chris@87 51 Misc Functions
Chris@87 52 --------------
Chris@87 53
Chris@87 54 .. autosummary::
Chris@87 55 :toctree: generated/
Chris@87 56
Chris@87 57 legfromroots create a Legendre series with specified roots.
Chris@87 58 legroots find the roots of a Legendre series.
Chris@87 59 legvander Vandermonde-like matrix for Legendre polynomials.
Chris@87 60 legvander2d Vandermonde-like matrix for 2D power series.
Chris@87 61 legvander3d Vandermonde-like matrix for 3D power series.
Chris@87 62 leggauss Gauss-Legendre quadrature, points and weights.
Chris@87 63 legweight Legendre weight function.
Chris@87 64 legcompanion symmetrized companion matrix in Legendre form.
Chris@87 65 legfit least-squares fit returning a Legendre series.
Chris@87 66 legtrim trim leading coefficients from a Legendre series.
Chris@87 67 legline Legendre series representing given straight line.
Chris@87 68 leg2poly convert a Legendre series to a polynomial.
Chris@87 69 poly2leg convert a polynomial to a Legendre series.
Chris@87 70
Chris@87 71 Classes
Chris@87 72 -------
Chris@87 73 Legendre A Legendre series class.
Chris@87 74
Chris@87 75 See also
Chris@87 76 --------
Chris@87 77 numpy.polynomial.polynomial
Chris@87 78 numpy.polynomial.chebyshev
Chris@87 79 numpy.polynomial.laguerre
Chris@87 80 numpy.polynomial.hermite
Chris@87 81 numpy.polynomial.hermite_e
Chris@87 82
Chris@87 83 """
Chris@87 84 from __future__ import division, absolute_import, print_function
Chris@87 85
Chris@87 86 import warnings
Chris@87 87 import numpy as np
Chris@87 88 import numpy.linalg as la
Chris@87 89
Chris@87 90 from . import polyutils as pu
Chris@87 91 from ._polybase import ABCPolyBase
Chris@87 92
Chris@87 93 __all__ = [
Chris@87 94 'legzero', 'legone', 'legx', 'legdomain', 'legline', 'legadd',
Chris@87 95 'legsub', 'legmulx', 'legmul', 'legdiv', 'legpow', 'legval', 'legder',
Chris@87 96 'legint', 'leg2poly', 'poly2leg', 'legfromroots', 'legvander',
Chris@87 97 'legfit', 'legtrim', 'legroots', 'Legendre', 'legval2d', 'legval3d',
Chris@87 98 'leggrid2d', 'leggrid3d', 'legvander2d', 'legvander3d', 'legcompanion',
Chris@87 99 'leggauss', 'legweight']
Chris@87 100
Chris@87 101 legtrim = pu.trimcoef
Chris@87 102
Chris@87 103
Chris@87 104 def poly2leg(pol):
Chris@87 105 """
Chris@87 106 Convert a polynomial to a Legendre series.
Chris@87 107
Chris@87 108 Convert an array representing the coefficients of a polynomial (relative
Chris@87 109 to the "standard" basis) ordered from lowest degree to highest, to an
Chris@87 110 array of the coefficients of the equivalent Legendre series, ordered
Chris@87 111 from lowest to highest degree.
Chris@87 112
Chris@87 113 Parameters
Chris@87 114 ----------
Chris@87 115 pol : array_like
Chris@87 116 1-D array containing the polynomial coefficients
Chris@87 117
Chris@87 118 Returns
Chris@87 119 -------
Chris@87 120 c : ndarray
Chris@87 121 1-D array containing the coefficients of the equivalent Legendre
Chris@87 122 series.
Chris@87 123
Chris@87 124 See Also
Chris@87 125 --------
Chris@87 126 leg2poly
Chris@87 127
Chris@87 128 Notes
Chris@87 129 -----
Chris@87 130 The easy way to do conversions between polynomial basis sets
Chris@87 131 is to use the convert method of a class instance.
Chris@87 132
Chris@87 133 Examples
Chris@87 134 --------
Chris@87 135 >>> from numpy import polynomial as P
Chris@87 136 >>> p = P.Polynomial(np.arange(4))
Chris@87 137 >>> p
Chris@87 138 Polynomial([ 0., 1., 2., 3.], [-1., 1.])
Chris@87 139 >>> c = P.Legendre(P.poly2leg(p.coef))
Chris@87 140 >>> c
Chris@87 141 Legendre([ 1. , 3.25, 1. , 0.75], [-1., 1.])
Chris@87 142
Chris@87 143 """
Chris@87 144 [pol] = pu.as_series([pol])
Chris@87 145 deg = len(pol) - 1
Chris@87 146 res = 0
Chris@87 147 for i in range(deg, -1, -1):
Chris@87 148 res = legadd(legmulx(res), pol[i])
Chris@87 149 return res
Chris@87 150
Chris@87 151
Chris@87 152 def leg2poly(c):
Chris@87 153 """
Chris@87 154 Convert a Legendre series to a polynomial.
Chris@87 155
Chris@87 156 Convert an array representing the coefficients of a Legendre series,
Chris@87 157 ordered from lowest degree to highest, to an array of the coefficients
Chris@87 158 of the equivalent polynomial (relative to the "standard" basis) ordered
Chris@87 159 from lowest to highest degree.
Chris@87 160
Chris@87 161 Parameters
Chris@87 162 ----------
Chris@87 163 c : array_like
Chris@87 164 1-D array containing the Legendre series coefficients, ordered
Chris@87 165 from lowest order term to highest.
Chris@87 166
Chris@87 167 Returns
Chris@87 168 -------
Chris@87 169 pol : ndarray
Chris@87 170 1-D array containing the coefficients of the equivalent polynomial
Chris@87 171 (relative to the "standard" basis) ordered from lowest order term
Chris@87 172 to highest.
Chris@87 173
Chris@87 174 See Also
Chris@87 175 --------
Chris@87 176 poly2leg
Chris@87 177
Chris@87 178 Notes
Chris@87 179 -----
Chris@87 180 The easy way to do conversions between polynomial basis sets
Chris@87 181 is to use the convert method of a class instance.
Chris@87 182
Chris@87 183 Examples
Chris@87 184 --------
Chris@87 185 >>> c = P.Legendre(range(4))
Chris@87 186 >>> c
Chris@87 187 Legendre([ 0., 1., 2., 3.], [-1., 1.])
Chris@87 188 >>> p = c.convert(kind=P.Polynomial)
Chris@87 189 >>> p
Chris@87 190 Polynomial([-1. , -3.5, 3. , 7.5], [-1., 1.])
Chris@87 191 >>> P.leg2poly(range(4))
Chris@87 192 array([-1. , -3.5, 3. , 7.5])
Chris@87 193
Chris@87 194
Chris@87 195 """
Chris@87 196 from .polynomial import polyadd, polysub, polymulx
Chris@87 197
Chris@87 198 [c] = pu.as_series([c])
Chris@87 199 n = len(c)
Chris@87 200 if n < 3:
Chris@87 201 return c
Chris@87 202 else:
Chris@87 203 c0 = c[-2]
Chris@87 204 c1 = c[-1]
Chris@87 205 # i is the current degree of c1
Chris@87 206 for i in range(n - 1, 1, -1):
Chris@87 207 tmp = c0
Chris@87 208 c0 = polysub(c[i - 2], (c1*(i - 1))/i)
Chris@87 209 c1 = polyadd(tmp, (polymulx(c1)*(2*i - 1))/i)
Chris@87 210 return polyadd(c0, polymulx(c1))
Chris@87 211
Chris@87 212 #
Chris@87 213 # These are constant arrays are of integer type so as to be compatible
Chris@87 214 # with the widest range of other types, such as Decimal.
Chris@87 215 #
Chris@87 216
Chris@87 217 # Legendre
Chris@87 218 legdomain = np.array([-1, 1])
Chris@87 219
Chris@87 220 # Legendre coefficients representing zero.
Chris@87 221 legzero = np.array([0])
Chris@87 222
Chris@87 223 # Legendre coefficients representing one.
Chris@87 224 legone = np.array([1])
Chris@87 225
Chris@87 226 # Legendre coefficients representing the identity x.
Chris@87 227 legx = np.array([0, 1])
Chris@87 228
Chris@87 229
Chris@87 230 def legline(off, scl):
Chris@87 231 """
Chris@87 232 Legendre series whose graph is a straight line.
Chris@87 233
Chris@87 234
Chris@87 235
Chris@87 236 Parameters
Chris@87 237 ----------
Chris@87 238 off, scl : scalars
Chris@87 239 The specified line is given by ``off + scl*x``.
Chris@87 240
Chris@87 241 Returns
Chris@87 242 -------
Chris@87 243 y : ndarray
Chris@87 244 This module's representation of the Legendre series for
Chris@87 245 ``off + scl*x``.
Chris@87 246
Chris@87 247 See Also
Chris@87 248 --------
Chris@87 249 polyline, chebline
Chris@87 250
Chris@87 251 Examples
Chris@87 252 --------
Chris@87 253 >>> import numpy.polynomial.legendre as L
Chris@87 254 >>> L.legline(3,2)
Chris@87 255 array([3, 2])
Chris@87 256 >>> L.legval(-3, L.legline(3,2)) # should be -3
Chris@87 257 -3.0
Chris@87 258
Chris@87 259 """
Chris@87 260 if scl != 0:
Chris@87 261 return np.array([off, scl])
Chris@87 262 else:
Chris@87 263 return np.array([off])
Chris@87 264
Chris@87 265
Chris@87 266 def legfromroots(roots):
Chris@87 267 """
Chris@87 268 Generate a Legendre series with given roots.
Chris@87 269
Chris@87 270 The function returns the coefficients of the polynomial
Chris@87 271
Chris@87 272 .. math:: p(x) = (x - r_0) * (x - r_1) * ... * (x - r_n),
Chris@87 273
Chris@87 274 in Legendre form, where the `r_n` are the roots specified in `roots`.
Chris@87 275 If a zero has multiplicity n, then it must appear in `roots` n times.
Chris@87 276 For instance, if 2 is a root of multiplicity three and 3 is a root of
Chris@87 277 multiplicity 2, then `roots` looks something like [2, 2, 2, 3, 3]. The
Chris@87 278 roots can appear in any order.
Chris@87 279
Chris@87 280 If the returned coefficients are `c`, then
Chris@87 281
Chris@87 282 .. math:: p(x) = c_0 + c_1 * L_1(x) + ... + c_n * L_n(x)
Chris@87 283
Chris@87 284 The coefficient of the last term is not generally 1 for monic
Chris@87 285 polynomials in Legendre form.
Chris@87 286
Chris@87 287 Parameters
Chris@87 288 ----------
Chris@87 289 roots : array_like
Chris@87 290 Sequence containing the roots.
Chris@87 291
Chris@87 292 Returns
Chris@87 293 -------
Chris@87 294 out : ndarray
Chris@87 295 1-D array of coefficients. If all roots are real then `out` is a
Chris@87 296 real array, if some of the roots are complex, then `out` is complex
Chris@87 297 even if all the coefficients in the result are real (see Examples
Chris@87 298 below).
Chris@87 299
Chris@87 300 See Also
Chris@87 301 --------
Chris@87 302 polyfromroots, chebfromroots, lagfromroots, hermfromroots,
Chris@87 303 hermefromroots.
Chris@87 304
Chris@87 305 Examples
Chris@87 306 --------
Chris@87 307 >>> import numpy.polynomial.legendre as L
Chris@87 308 >>> L.legfromroots((-1,0,1)) # x^3 - x relative to the standard basis
Chris@87 309 array([ 0. , -0.4, 0. , 0.4])
Chris@87 310 >>> j = complex(0,1)
Chris@87 311 >>> L.legfromroots((-j,j)) # x^2 + 1 relative to the standard basis
Chris@87 312 array([ 1.33333333+0.j, 0.00000000+0.j, 0.66666667+0.j])
Chris@87 313
Chris@87 314 """
Chris@87 315 if len(roots) == 0:
Chris@87 316 return np.ones(1)
Chris@87 317 else:
Chris@87 318 [roots] = pu.as_series([roots], trim=False)
Chris@87 319 roots.sort()
Chris@87 320 p = [legline(-r, 1) for r in roots]
Chris@87 321 n = len(p)
Chris@87 322 while n > 1:
Chris@87 323 m, r = divmod(n, 2)
Chris@87 324 tmp = [legmul(p[i], p[i+m]) for i in range(m)]
Chris@87 325 if r:
Chris@87 326 tmp[0] = legmul(tmp[0], p[-1])
Chris@87 327 p = tmp
Chris@87 328 n = m
Chris@87 329 return p[0]
Chris@87 330
Chris@87 331
Chris@87 332 def legadd(c1, c2):
Chris@87 333 """
Chris@87 334 Add one Legendre series to another.
Chris@87 335
Chris@87 336 Returns the sum of two Legendre series `c1` + `c2`. The arguments
Chris@87 337 are sequences of coefficients ordered from lowest order term to
Chris@87 338 highest, i.e., [1,2,3] represents the series ``P_0 + 2*P_1 + 3*P_2``.
Chris@87 339
Chris@87 340 Parameters
Chris@87 341 ----------
Chris@87 342 c1, c2 : array_like
Chris@87 343 1-D arrays of Legendre series coefficients ordered from low to
Chris@87 344 high.
Chris@87 345
Chris@87 346 Returns
Chris@87 347 -------
Chris@87 348 out : ndarray
Chris@87 349 Array representing the Legendre series of their sum.
Chris@87 350
Chris@87 351 See Also
Chris@87 352 --------
Chris@87 353 legsub, legmul, legdiv, legpow
Chris@87 354
Chris@87 355 Notes
Chris@87 356 -----
Chris@87 357 Unlike multiplication, division, etc., the sum of two Legendre series
Chris@87 358 is a Legendre series (without having to "reproject" the result onto
Chris@87 359 the basis set) so addition, just like that of "standard" polynomials,
Chris@87 360 is simply "component-wise."
Chris@87 361
Chris@87 362 Examples
Chris@87 363 --------
Chris@87 364 >>> from numpy.polynomial import legendre as L
Chris@87 365 >>> c1 = (1,2,3)
Chris@87 366 >>> c2 = (3,2,1)
Chris@87 367 >>> L.legadd(c1,c2)
Chris@87 368 array([ 4., 4., 4.])
Chris@87 369
Chris@87 370 """
Chris@87 371 # c1, c2 are trimmed copies
Chris@87 372 [c1, c2] = pu.as_series([c1, c2])
Chris@87 373 if len(c1) > len(c2):
Chris@87 374 c1[:c2.size] += c2
Chris@87 375 ret = c1
Chris@87 376 else:
Chris@87 377 c2[:c1.size] += c1
Chris@87 378 ret = c2
Chris@87 379 return pu.trimseq(ret)
Chris@87 380
Chris@87 381
Chris@87 382 def legsub(c1, c2):
Chris@87 383 """
Chris@87 384 Subtract one Legendre series from another.
Chris@87 385
Chris@87 386 Returns the difference of two Legendre series `c1` - `c2`. The
Chris@87 387 sequences of coefficients are from lowest order term to highest, i.e.,
Chris@87 388 [1,2,3] represents the series ``P_0 + 2*P_1 + 3*P_2``.
Chris@87 389
Chris@87 390 Parameters
Chris@87 391 ----------
Chris@87 392 c1, c2 : array_like
Chris@87 393 1-D arrays of Legendre series coefficients ordered from low to
Chris@87 394 high.
Chris@87 395
Chris@87 396 Returns
Chris@87 397 -------
Chris@87 398 out : ndarray
Chris@87 399 Of Legendre series coefficients representing their difference.
Chris@87 400
Chris@87 401 See Also
Chris@87 402 --------
Chris@87 403 legadd, legmul, legdiv, legpow
Chris@87 404
Chris@87 405 Notes
Chris@87 406 -----
Chris@87 407 Unlike multiplication, division, etc., the difference of two Legendre
Chris@87 408 series is a Legendre series (without having to "reproject" the result
Chris@87 409 onto the basis set) so subtraction, just like that of "standard"
Chris@87 410 polynomials, is simply "component-wise."
Chris@87 411
Chris@87 412 Examples
Chris@87 413 --------
Chris@87 414 >>> from numpy.polynomial import legendre as L
Chris@87 415 >>> c1 = (1,2,3)
Chris@87 416 >>> c2 = (3,2,1)
Chris@87 417 >>> L.legsub(c1,c2)
Chris@87 418 array([-2., 0., 2.])
Chris@87 419 >>> L.legsub(c2,c1) # -C.legsub(c1,c2)
Chris@87 420 array([ 2., 0., -2.])
Chris@87 421
Chris@87 422 """
Chris@87 423 # c1, c2 are trimmed copies
Chris@87 424 [c1, c2] = pu.as_series([c1, c2])
Chris@87 425 if len(c1) > len(c2):
Chris@87 426 c1[:c2.size] -= c2
Chris@87 427 ret = c1
Chris@87 428 else:
Chris@87 429 c2 = -c2
Chris@87 430 c2[:c1.size] += c1
Chris@87 431 ret = c2
Chris@87 432 return pu.trimseq(ret)
Chris@87 433
Chris@87 434
Chris@87 435 def legmulx(c):
Chris@87 436 """Multiply a Legendre series by x.
Chris@87 437
Chris@87 438 Multiply the Legendre series `c` by x, where x is the independent
Chris@87 439 variable.
Chris@87 440
Chris@87 441
Chris@87 442 Parameters
Chris@87 443 ----------
Chris@87 444 c : array_like
Chris@87 445 1-D array of Legendre series coefficients ordered from low to
Chris@87 446 high.
Chris@87 447
Chris@87 448 Returns
Chris@87 449 -------
Chris@87 450 out : ndarray
Chris@87 451 Array representing the result of the multiplication.
Chris@87 452
Chris@87 453 Notes
Chris@87 454 -----
Chris@87 455 The multiplication uses the recursion relationship for Legendre
Chris@87 456 polynomials in the form
Chris@87 457
Chris@87 458 .. math::
Chris@87 459
Chris@87 460 xP_i(x) = ((i + 1)*P_{i + 1}(x) + i*P_{i - 1}(x))/(2i + 1)
Chris@87 461
Chris@87 462 """
Chris@87 463 # c is a trimmed copy
Chris@87 464 [c] = pu.as_series([c])
Chris@87 465 # The zero series needs special treatment
Chris@87 466 if len(c) == 1 and c[0] == 0:
Chris@87 467 return c
Chris@87 468
Chris@87 469 prd = np.empty(len(c) + 1, dtype=c.dtype)
Chris@87 470 prd[0] = c[0]*0
Chris@87 471 prd[1] = c[0]
Chris@87 472 for i in range(1, len(c)):
Chris@87 473 j = i + 1
Chris@87 474 k = i - 1
Chris@87 475 s = i + j
Chris@87 476 prd[j] = (c[i]*j)/s
Chris@87 477 prd[k] += (c[i]*i)/s
Chris@87 478 return prd
Chris@87 479
Chris@87 480
Chris@87 481 def legmul(c1, c2):
Chris@87 482 """
Chris@87 483 Multiply one Legendre series by another.
Chris@87 484
Chris@87 485 Returns the product of two Legendre series `c1` * `c2`. The arguments
Chris@87 486 are sequences of coefficients, from lowest order "term" to highest,
Chris@87 487 e.g., [1,2,3] represents the series ``P_0 + 2*P_1 + 3*P_2``.
Chris@87 488
Chris@87 489 Parameters
Chris@87 490 ----------
Chris@87 491 c1, c2 : array_like
Chris@87 492 1-D arrays of Legendre series coefficients ordered from low to
Chris@87 493 high.
Chris@87 494
Chris@87 495 Returns
Chris@87 496 -------
Chris@87 497 out : ndarray
Chris@87 498 Of Legendre series coefficients representing their product.
Chris@87 499
Chris@87 500 See Also
Chris@87 501 --------
Chris@87 502 legadd, legsub, legdiv, legpow
Chris@87 503
Chris@87 504 Notes
Chris@87 505 -----
Chris@87 506 In general, the (polynomial) product of two C-series results in terms
Chris@87 507 that are not in the Legendre polynomial basis set. Thus, to express
Chris@87 508 the product as a Legendre series, it is necessary to "reproject" the
Chris@87 509 product onto said basis set, which may produce "unintuitive" (but
Chris@87 510 correct) results; see Examples section below.
Chris@87 511
Chris@87 512 Examples
Chris@87 513 --------
Chris@87 514 >>> from numpy.polynomial import legendre as L
Chris@87 515 >>> c1 = (1,2,3)
Chris@87 516 >>> c2 = (3,2)
Chris@87 517 >>> P.legmul(c1,c2) # multiplication requires "reprojection"
Chris@87 518 array([ 4.33333333, 10.4 , 11.66666667, 3.6 ])
Chris@87 519
Chris@87 520 """
Chris@87 521 # s1, s2 are trimmed copies
Chris@87 522 [c1, c2] = pu.as_series([c1, c2])
Chris@87 523
Chris@87 524 if len(c1) > len(c2):
Chris@87 525 c = c2
Chris@87 526 xs = c1
Chris@87 527 else:
Chris@87 528 c = c1
Chris@87 529 xs = c2
Chris@87 530
Chris@87 531 if len(c) == 1:
Chris@87 532 c0 = c[0]*xs
Chris@87 533 c1 = 0
Chris@87 534 elif len(c) == 2:
Chris@87 535 c0 = c[0]*xs
Chris@87 536 c1 = c[1]*xs
Chris@87 537 else:
Chris@87 538 nd = len(c)
Chris@87 539 c0 = c[-2]*xs
Chris@87 540 c1 = c[-1]*xs
Chris@87 541 for i in range(3, len(c) + 1):
Chris@87 542 tmp = c0
Chris@87 543 nd = nd - 1
Chris@87 544 c0 = legsub(c[-i]*xs, (c1*(nd - 1))/nd)
Chris@87 545 c1 = legadd(tmp, (legmulx(c1)*(2*nd - 1))/nd)
Chris@87 546 return legadd(c0, legmulx(c1))
Chris@87 547
Chris@87 548
Chris@87 549 def legdiv(c1, c2):
Chris@87 550 """
Chris@87 551 Divide one Legendre series by another.
Chris@87 552
Chris@87 553 Returns the quotient-with-remainder of two Legendre series
Chris@87 554 `c1` / `c2`. The arguments are sequences of coefficients from lowest
Chris@87 555 order "term" to highest, e.g., [1,2,3] represents the series
Chris@87 556 ``P_0 + 2*P_1 + 3*P_2``.
Chris@87 557
Chris@87 558 Parameters
Chris@87 559 ----------
Chris@87 560 c1, c2 : array_like
Chris@87 561 1-D arrays of Legendre series coefficients ordered from low to
Chris@87 562 high.
Chris@87 563
Chris@87 564 Returns
Chris@87 565 -------
Chris@87 566 quo, rem : ndarrays
Chris@87 567 Of Legendre series coefficients representing the quotient and
Chris@87 568 remainder.
Chris@87 569
Chris@87 570 See Also
Chris@87 571 --------
Chris@87 572 legadd, legsub, legmul, legpow
Chris@87 573
Chris@87 574 Notes
Chris@87 575 -----
Chris@87 576 In general, the (polynomial) division of one Legendre series by another
Chris@87 577 results in quotient and remainder terms that are not in the Legendre
Chris@87 578 polynomial basis set. Thus, to express these results as a Legendre
Chris@87 579 series, it is necessary to "reproject" the results onto the Legendre
Chris@87 580 basis set, which may produce "unintuitive" (but correct) results; see
Chris@87 581 Examples section below.
Chris@87 582
Chris@87 583 Examples
Chris@87 584 --------
Chris@87 585 >>> from numpy.polynomial import legendre as L
Chris@87 586 >>> c1 = (1,2,3)
Chris@87 587 >>> c2 = (3,2,1)
Chris@87 588 >>> L.legdiv(c1,c2) # quotient "intuitive," remainder not
Chris@87 589 (array([ 3.]), array([-8., -4.]))
Chris@87 590 >>> c2 = (0,1,2,3)
Chris@87 591 >>> L.legdiv(c2,c1) # neither "intuitive"
Chris@87 592 (array([-0.07407407, 1.66666667]), array([-1.03703704, -2.51851852]))
Chris@87 593
Chris@87 594 """
Chris@87 595 # c1, c2 are trimmed copies
Chris@87 596 [c1, c2] = pu.as_series([c1, c2])
Chris@87 597 if c2[-1] == 0:
Chris@87 598 raise ZeroDivisionError()
Chris@87 599
Chris@87 600 lc1 = len(c1)
Chris@87 601 lc2 = len(c2)
Chris@87 602 if lc1 < lc2:
Chris@87 603 return c1[:1]*0, c1
Chris@87 604 elif lc2 == 1:
Chris@87 605 return c1/c2[-1], c1[:1]*0
Chris@87 606 else:
Chris@87 607 quo = np.empty(lc1 - lc2 + 1, dtype=c1.dtype)
Chris@87 608 rem = c1
Chris@87 609 for i in range(lc1 - lc2, - 1, -1):
Chris@87 610 p = legmul([0]*i + [1], c2)
Chris@87 611 q = rem[-1]/p[-1]
Chris@87 612 rem = rem[:-1] - q*p[:-1]
Chris@87 613 quo[i] = q
Chris@87 614 return quo, pu.trimseq(rem)
Chris@87 615
Chris@87 616
Chris@87 617 def legpow(c, pow, maxpower=16):
Chris@87 618 """Raise a Legendre series to a power.
Chris@87 619
Chris@87 620 Returns the Legendre series `c` raised to the power `pow`. The
Chris@87 621 arguement `c` is a sequence of coefficients ordered from low to high.
Chris@87 622 i.e., [1,2,3] is the series ``P_0 + 2*P_1 + 3*P_2.``
Chris@87 623
Chris@87 624 Parameters
Chris@87 625 ----------
Chris@87 626 c : array_like
Chris@87 627 1-D array of Legendre series coefficients ordered from low to
Chris@87 628 high.
Chris@87 629 pow : integer
Chris@87 630 Power to which the series will be raised
Chris@87 631 maxpower : integer, optional
Chris@87 632 Maximum power allowed. This is mainly to limit growth of the series
Chris@87 633 to unmanageable size. Default is 16
Chris@87 634
Chris@87 635 Returns
Chris@87 636 -------
Chris@87 637 coef : ndarray
Chris@87 638 Legendre series of power.
Chris@87 639
Chris@87 640 See Also
Chris@87 641 --------
Chris@87 642 legadd, legsub, legmul, legdiv
Chris@87 643
Chris@87 644 Examples
Chris@87 645 --------
Chris@87 646
Chris@87 647 """
Chris@87 648 # c is a trimmed copy
Chris@87 649 [c] = pu.as_series([c])
Chris@87 650 power = int(pow)
Chris@87 651 if power != pow or power < 0:
Chris@87 652 raise ValueError("Power must be a non-negative integer.")
Chris@87 653 elif maxpower is not None and power > maxpower:
Chris@87 654 raise ValueError("Power is too large")
Chris@87 655 elif power == 0:
Chris@87 656 return np.array([1], dtype=c.dtype)
Chris@87 657 elif power == 1:
Chris@87 658 return c
Chris@87 659 else:
Chris@87 660 # This can be made more efficient by using powers of two
Chris@87 661 # in the usual way.
Chris@87 662 prd = c
Chris@87 663 for i in range(2, power + 1):
Chris@87 664 prd = legmul(prd, c)
Chris@87 665 return prd
Chris@87 666
Chris@87 667
Chris@87 668 def legder(c, m=1, scl=1, axis=0):
Chris@87 669 """
Chris@87 670 Differentiate a Legendre series.
Chris@87 671
Chris@87 672 Returns the Legendre series coefficients `c` differentiated `m` times
Chris@87 673 along `axis`. At each iteration the result is multiplied by `scl` (the
Chris@87 674 scaling factor is for use in a linear change of variable). The argument
Chris@87 675 `c` is an array of coefficients from low to high degree along each
Chris@87 676 axis, e.g., [1,2,3] represents the series ``1*L_0 + 2*L_1 + 3*L_2``
Chris@87 677 while [[1,2],[1,2]] represents ``1*L_0(x)*L_0(y) + 1*L_1(x)*L_0(y) +
Chris@87 678 2*L_0(x)*L_1(y) + 2*L_1(x)*L_1(y)`` if axis=0 is ``x`` and axis=1 is
Chris@87 679 ``y``.
Chris@87 680
Chris@87 681 Parameters
Chris@87 682 ----------
Chris@87 683 c : array_like
Chris@87 684 Array of Legendre series coefficients. If c is multidimensional the
Chris@87 685 different axis correspond to different variables with the degree in
Chris@87 686 each axis given by the corresponding index.
Chris@87 687 m : int, optional
Chris@87 688 Number of derivatives taken, must be non-negative. (Default: 1)
Chris@87 689 scl : scalar, optional
Chris@87 690 Each differentiation is multiplied by `scl`. The end result is
Chris@87 691 multiplication by ``scl**m``. This is for use in a linear change of
Chris@87 692 variable. (Default: 1)
Chris@87 693 axis : int, optional
Chris@87 694 Axis over which the derivative is taken. (Default: 0).
Chris@87 695
Chris@87 696 .. versionadded:: 1.7.0
Chris@87 697
Chris@87 698 Returns
Chris@87 699 -------
Chris@87 700 der : ndarray
Chris@87 701 Legendre series of the derivative.
Chris@87 702
Chris@87 703 See Also
Chris@87 704 --------
Chris@87 705 legint
Chris@87 706
Chris@87 707 Notes
Chris@87 708 -----
Chris@87 709 In general, the result of differentiating a Legendre series does not
Chris@87 710 resemble the same operation on a power series. Thus the result of this
Chris@87 711 function may be "unintuitive," albeit correct; see Examples section
Chris@87 712 below.
Chris@87 713
Chris@87 714 Examples
Chris@87 715 --------
Chris@87 716 >>> from numpy.polynomial import legendre as L
Chris@87 717 >>> c = (1,2,3,4)
Chris@87 718 >>> L.legder(c)
Chris@87 719 array([ 6., 9., 20.])
Chris@87 720 >>> L.legder(c, 3)
Chris@87 721 array([ 60.])
Chris@87 722 >>> L.legder(c, scl=-1)
Chris@87 723 array([ -6., -9., -20.])
Chris@87 724 >>> L.legder(c, 2,-1)
Chris@87 725 array([ 9., 60.])
Chris@87 726
Chris@87 727 """
Chris@87 728 c = np.array(c, ndmin=1, copy=1)
Chris@87 729 if c.dtype.char in '?bBhHiIlLqQpP':
Chris@87 730 c = c.astype(np.double)
Chris@87 731 cnt, iaxis = [int(t) for t in [m, axis]]
Chris@87 732
Chris@87 733 if cnt != m:
Chris@87 734 raise ValueError("The order of derivation must be integer")
Chris@87 735 if cnt < 0:
Chris@87 736 raise ValueError("The order of derivation must be non-negative")
Chris@87 737 if iaxis != axis:
Chris@87 738 raise ValueError("The axis must be integer")
Chris@87 739 if not -c.ndim <= iaxis < c.ndim:
Chris@87 740 raise ValueError("The axis is out of range")
Chris@87 741 if iaxis < 0:
Chris@87 742 iaxis += c.ndim
Chris@87 743
Chris@87 744 if cnt == 0:
Chris@87 745 return c
Chris@87 746
Chris@87 747 c = np.rollaxis(c, iaxis)
Chris@87 748 n = len(c)
Chris@87 749 if cnt >= n:
Chris@87 750 c = c[:1]*0
Chris@87 751 else:
Chris@87 752 for i in range(cnt):
Chris@87 753 n = n - 1
Chris@87 754 c *= scl
Chris@87 755 der = np.empty((n,) + c.shape[1:], dtype=c.dtype)
Chris@87 756 for j in range(n, 2, -1):
Chris@87 757 der[j - 1] = (2*j - 1)*c[j]
Chris@87 758 c[j - 2] += c[j]
Chris@87 759 if n > 1:
Chris@87 760 der[1] = 3*c[2]
Chris@87 761 der[0] = c[1]
Chris@87 762 c = der
Chris@87 763 c = np.rollaxis(c, 0, iaxis + 1)
Chris@87 764 return c
Chris@87 765
Chris@87 766
Chris@87 767 def legint(c, m=1, k=[], lbnd=0, scl=1, axis=0):
Chris@87 768 """
Chris@87 769 Integrate a Legendre series.
Chris@87 770
Chris@87 771 Returns the Legendre series coefficients `c` integrated `m` times from
Chris@87 772 `lbnd` along `axis`. At each iteration the resulting series is
Chris@87 773 **multiplied** by `scl` and an integration constant, `k`, is added.
Chris@87 774 The scaling factor is for use in a linear change of variable. ("Buyer
Chris@87 775 beware": note that, depending on what one is doing, one may want `scl`
Chris@87 776 to be the reciprocal of what one might expect; for more information,
Chris@87 777 see the Notes section below.) The argument `c` is an array of
Chris@87 778 coefficients from low to high degree along each axis, e.g., [1,2,3]
Chris@87 779 represents the series ``L_0 + 2*L_1 + 3*L_2`` while [[1,2],[1,2]]
Chris@87 780 represents ``1*L_0(x)*L_0(y) + 1*L_1(x)*L_0(y) + 2*L_0(x)*L_1(y) +
Chris@87 781 2*L_1(x)*L_1(y)`` if axis=0 is ``x`` and axis=1 is ``y``.
Chris@87 782
Chris@87 783 Parameters
Chris@87 784 ----------
Chris@87 785 c : array_like
Chris@87 786 Array of Legendre series coefficients. If c is multidimensional the
Chris@87 787 different axis correspond to different variables with the degree in
Chris@87 788 each axis given by the corresponding index.
Chris@87 789 m : int, optional
Chris@87 790 Order of integration, must be positive. (Default: 1)
Chris@87 791 k : {[], list, scalar}, optional
Chris@87 792 Integration constant(s). The value of the first integral at
Chris@87 793 ``lbnd`` is the first value in the list, the value of the second
Chris@87 794 integral at ``lbnd`` is the second value, etc. If ``k == []`` (the
Chris@87 795 default), all constants are set to zero. If ``m == 1``, a single
Chris@87 796 scalar can be given instead of a list.
Chris@87 797 lbnd : scalar, optional
Chris@87 798 The lower bound of the integral. (Default: 0)
Chris@87 799 scl : scalar, optional
Chris@87 800 Following each integration the result is *multiplied* by `scl`
Chris@87 801 before the integration constant is added. (Default: 1)
Chris@87 802 axis : int, optional
Chris@87 803 Axis over which the integral is taken. (Default: 0).
Chris@87 804
Chris@87 805 .. versionadded:: 1.7.0
Chris@87 806
Chris@87 807 Returns
Chris@87 808 -------
Chris@87 809 S : ndarray
Chris@87 810 Legendre series coefficient array of the integral.
Chris@87 811
Chris@87 812 Raises
Chris@87 813 ------
Chris@87 814 ValueError
Chris@87 815 If ``m < 0``, ``len(k) > m``, ``np.isscalar(lbnd) == False``, or
Chris@87 816 ``np.isscalar(scl) == False``.
Chris@87 817
Chris@87 818 See Also
Chris@87 819 --------
Chris@87 820 legder
Chris@87 821
Chris@87 822 Notes
Chris@87 823 -----
Chris@87 824 Note that the result of each integration is *multiplied* by `scl`.
Chris@87 825 Why is this important to note? Say one is making a linear change of
Chris@87 826 variable :math:`u = ax + b` in an integral relative to `x`. Then
Chris@87 827 .. math::`dx = du/a`, so one will need to set `scl` equal to
Chris@87 828 :math:`1/a` - perhaps not what one would have first thought.
Chris@87 829
Chris@87 830 Also note that, in general, the result of integrating a C-series needs
Chris@87 831 to be "reprojected" onto the C-series basis set. Thus, typically,
Chris@87 832 the result of this function is "unintuitive," albeit correct; see
Chris@87 833 Examples section below.
Chris@87 834
Chris@87 835 Examples
Chris@87 836 --------
Chris@87 837 >>> from numpy.polynomial import legendre as L
Chris@87 838 >>> c = (1,2,3)
Chris@87 839 >>> L.legint(c)
Chris@87 840 array([ 0.33333333, 0.4 , 0.66666667, 0.6 ])
Chris@87 841 >>> L.legint(c, 3)
Chris@87 842 array([ 1.66666667e-02, -1.78571429e-02, 4.76190476e-02,
Chris@87 843 -1.73472348e-18, 1.90476190e-02, 9.52380952e-03])
Chris@87 844 >>> L.legint(c, k=3)
Chris@87 845 array([ 3.33333333, 0.4 , 0.66666667, 0.6 ])
Chris@87 846 >>> L.legint(c, lbnd=-2)
Chris@87 847 array([ 7.33333333, 0.4 , 0.66666667, 0.6 ])
Chris@87 848 >>> L.legint(c, scl=2)
Chris@87 849 array([ 0.66666667, 0.8 , 1.33333333, 1.2 ])
Chris@87 850
Chris@87 851 """
Chris@87 852 c = np.array(c, ndmin=1, copy=1)
Chris@87 853 if c.dtype.char in '?bBhHiIlLqQpP':
Chris@87 854 c = c.astype(np.double)
Chris@87 855 if not np.iterable(k):
Chris@87 856 k = [k]
Chris@87 857 cnt, iaxis = [int(t) for t in [m, axis]]
Chris@87 858
Chris@87 859 if cnt != m:
Chris@87 860 raise ValueError("The order of integration must be integer")
Chris@87 861 if cnt < 0:
Chris@87 862 raise ValueError("The order of integration must be non-negative")
Chris@87 863 if len(k) > cnt:
Chris@87 864 raise ValueError("Too many integration constants")
Chris@87 865 if iaxis != axis:
Chris@87 866 raise ValueError("The axis must be integer")
Chris@87 867 if not -c.ndim <= iaxis < c.ndim:
Chris@87 868 raise ValueError("The axis is out of range")
Chris@87 869 if iaxis < 0:
Chris@87 870 iaxis += c.ndim
Chris@87 871
Chris@87 872 if cnt == 0:
Chris@87 873 return c
Chris@87 874
Chris@87 875 c = np.rollaxis(c, iaxis)
Chris@87 876 k = list(k) + [0]*(cnt - len(k))
Chris@87 877 for i in range(cnt):
Chris@87 878 n = len(c)
Chris@87 879 c *= scl
Chris@87 880 if n == 1 and np.all(c[0] == 0):
Chris@87 881 c[0] += k[i]
Chris@87 882 else:
Chris@87 883 tmp = np.empty((n + 1,) + c.shape[1:], dtype=c.dtype)
Chris@87 884 tmp[0] = c[0]*0
Chris@87 885 tmp[1] = c[0]
Chris@87 886 if n > 1:
Chris@87 887 tmp[2] = c[1]/3
Chris@87 888 for j in range(2, n):
Chris@87 889 t = c[j]/(2*j + 1)
Chris@87 890 tmp[j + 1] = t
Chris@87 891 tmp[j - 1] -= t
Chris@87 892 tmp[0] += k[i] - legval(lbnd, tmp)
Chris@87 893 c = tmp
Chris@87 894 c = np.rollaxis(c, 0, iaxis + 1)
Chris@87 895 return c
Chris@87 896
Chris@87 897
Chris@87 898 def legval(x, c, tensor=True):
Chris@87 899 """
Chris@87 900 Evaluate a Legendre series at points x.
Chris@87 901
Chris@87 902 If `c` is of length `n + 1`, this function returns the value:
Chris@87 903
Chris@87 904 .. math:: p(x) = c_0 * L_0(x) + c_1 * L_1(x) + ... + c_n * L_n(x)
Chris@87 905
Chris@87 906 The parameter `x` is converted to an array only if it is a tuple or a
Chris@87 907 list, otherwise it is treated as a scalar. In either case, either `x`
Chris@87 908 or its elements must support multiplication and addition both with
Chris@87 909 themselves and with the elements of `c`.
Chris@87 910
Chris@87 911 If `c` is a 1-D array, then `p(x)` will have the same shape as `x`. If
Chris@87 912 `c` is multidimensional, then the shape of the result depends on the
Chris@87 913 value of `tensor`. If `tensor` is true the shape will be c.shape[1:] +
Chris@87 914 x.shape. If `tensor` is false the shape will be c.shape[1:]. Note that
Chris@87 915 scalars have shape (,).
Chris@87 916
Chris@87 917 Trailing zeros in the coefficients will be used in the evaluation, so
Chris@87 918 they should be avoided if efficiency is a concern.
Chris@87 919
Chris@87 920 Parameters
Chris@87 921 ----------
Chris@87 922 x : array_like, compatible object
Chris@87 923 If `x` is a list or tuple, it is converted to an ndarray, otherwise
Chris@87 924 it is left unchanged and treated as a scalar. In either case, `x`
Chris@87 925 or its elements must support addition and multiplication with
Chris@87 926 with themselves and with the elements of `c`.
Chris@87 927 c : array_like
Chris@87 928 Array of coefficients ordered so that the coefficients for terms of
Chris@87 929 degree n are contained in c[n]. If `c` is multidimensional the
Chris@87 930 remaining indices enumerate multiple polynomials. In the two
Chris@87 931 dimensional case the coefficients may be thought of as stored in
Chris@87 932 the columns of `c`.
Chris@87 933 tensor : boolean, optional
Chris@87 934 If True, the shape of the coefficient array is extended with ones
Chris@87 935 on the right, one for each dimension of `x`. Scalars have dimension 0
Chris@87 936 for this action. The result is that every column of coefficients in
Chris@87 937 `c` is evaluated for every element of `x`. If False, `x` is broadcast
Chris@87 938 over the columns of `c` for the evaluation. This keyword is useful
Chris@87 939 when `c` is multidimensional. The default value is True.
Chris@87 940
Chris@87 941 .. versionadded:: 1.7.0
Chris@87 942
Chris@87 943 Returns
Chris@87 944 -------
Chris@87 945 values : ndarray, algebra_like
Chris@87 946 The shape of the return value is described above.
Chris@87 947
Chris@87 948 See Also
Chris@87 949 --------
Chris@87 950 legval2d, leggrid2d, legval3d, leggrid3d
Chris@87 951
Chris@87 952 Notes
Chris@87 953 -----
Chris@87 954 The evaluation uses Clenshaw recursion, aka synthetic division.
Chris@87 955
Chris@87 956 Examples
Chris@87 957 --------
Chris@87 958
Chris@87 959 """
Chris@87 960 c = np.array(c, ndmin=1, copy=0)
Chris@87 961 if c.dtype.char in '?bBhHiIlLqQpP':
Chris@87 962 c = c.astype(np.double)
Chris@87 963 if isinstance(x, (tuple, list)):
Chris@87 964 x = np.asarray(x)
Chris@87 965 if isinstance(x, np.ndarray) and tensor:
Chris@87 966 c = c.reshape(c.shape + (1,)*x.ndim)
Chris@87 967
Chris@87 968 if len(c) == 1:
Chris@87 969 c0 = c[0]
Chris@87 970 c1 = 0
Chris@87 971 elif len(c) == 2:
Chris@87 972 c0 = c[0]
Chris@87 973 c1 = c[1]
Chris@87 974 else:
Chris@87 975 nd = len(c)
Chris@87 976 c0 = c[-2]
Chris@87 977 c1 = c[-1]
Chris@87 978 for i in range(3, len(c) + 1):
Chris@87 979 tmp = c0
Chris@87 980 nd = nd - 1
Chris@87 981 c0 = c[-i] - (c1*(nd - 1))/nd
Chris@87 982 c1 = tmp + (c1*x*(2*nd - 1))/nd
Chris@87 983 return c0 + c1*x
Chris@87 984
Chris@87 985
Chris@87 986 def legval2d(x, y, c):
Chris@87 987 """
Chris@87 988 Evaluate a 2-D Legendre series at points (x, y).
Chris@87 989
Chris@87 990 This function returns the values:
Chris@87 991
Chris@87 992 .. math:: p(x,y) = \\sum_{i,j} c_{i,j} * L_i(x) * L_j(y)
Chris@87 993
Chris@87 994 The parameters `x` and `y` are converted to arrays only if they are
Chris@87 995 tuples or a lists, otherwise they are treated as a scalars and they
Chris@87 996 must have the same shape after conversion. In either case, either `x`
Chris@87 997 and `y` or their elements must support multiplication and addition both
Chris@87 998 with themselves and with the elements of `c`.
Chris@87 999
Chris@87 1000 If `c` is a 1-D array a one is implicitly appended to its shape to make
Chris@87 1001 it 2-D. The shape of the result will be c.shape[2:] + x.shape.
Chris@87 1002
Chris@87 1003 Parameters
Chris@87 1004 ----------
Chris@87 1005 x, y : array_like, compatible objects
Chris@87 1006 The two dimensional series is evaluated at the points `(x, y)`,
Chris@87 1007 where `x` and `y` must have the same shape. If `x` or `y` is a list
Chris@87 1008 or tuple, it is first converted to an ndarray, otherwise it is left
Chris@87 1009 unchanged and if it isn't an ndarray it is treated as a scalar.
Chris@87 1010 c : array_like
Chris@87 1011 Array of coefficients ordered so that the coefficient of the term
Chris@87 1012 of multi-degree i,j is contained in ``c[i,j]``. If `c` has
Chris@87 1013 dimension greater than two the remaining indices enumerate multiple
Chris@87 1014 sets of coefficients.
Chris@87 1015
Chris@87 1016 Returns
Chris@87 1017 -------
Chris@87 1018 values : ndarray, compatible object
Chris@87 1019 The values of the two dimensional Legendre series at points formed
Chris@87 1020 from pairs of corresponding values from `x` and `y`.
Chris@87 1021
Chris@87 1022 See Also
Chris@87 1023 --------
Chris@87 1024 legval, leggrid2d, legval3d, leggrid3d
Chris@87 1025
Chris@87 1026 Notes
Chris@87 1027 -----
Chris@87 1028
Chris@87 1029 .. versionadded::1.7.0
Chris@87 1030
Chris@87 1031 """
Chris@87 1032 try:
Chris@87 1033 x, y = np.array((x, y), copy=0)
Chris@87 1034 except:
Chris@87 1035 raise ValueError('x, y are incompatible')
Chris@87 1036
Chris@87 1037 c = legval(x, c)
Chris@87 1038 c = legval(y, c, tensor=False)
Chris@87 1039 return c
Chris@87 1040
Chris@87 1041
Chris@87 1042 def leggrid2d(x, y, c):
Chris@87 1043 """
Chris@87 1044 Evaluate a 2-D Legendre series on the Cartesian product of x and y.
Chris@87 1045
Chris@87 1046 This function returns the values:
Chris@87 1047
Chris@87 1048 .. math:: p(a,b) = \sum_{i,j} c_{i,j} * L_i(a) * L_j(b)
Chris@87 1049
Chris@87 1050 where the points `(a, b)` consist of all pairs formed by taking
Chris@87 1051 `a` from `x` and `b` from `y`. The resulting points form a grid with
Chris@87 1052 `x` in the first dimension and `y` in the second.
Chris@87 1053
Chris@87 1054 The parameters `x` and `y` are converted to arrays only if they are
Chris@87 1055 tuples or a lists, otherwise they are treated as a scalars. In either
Chris@87 1056 case, either `x` and `y` or their elements must support multiplication
Chris@87 1057 and addition both with themselves and with the elements of `c`.
Chris@87 1058
Chris@87 1059 If `c` has fewer than two dimensions, ones are implicitly appended to
Chris@87 1060 its shape to make it 2-D. The shape of the result will be c.shape[2:] +
Chris@87 1061 x.shape + y.shape.
Chris@87 1062
Chris@87 1063 Parameters
Chris@87 1064 ----------
Chris@87 1065 x, y : array_like, compatible objects
Chris@87 1066 The two dimensional series is evaluated at the points in the
Chris@87 1067 Cartesian product of `x` and `y`. If `x` or `y` is a list or
Chris@87 1068 tuple, it is first converted to an ndarray, otherwise it is left
Chris@87 1069 unchanged and, if it isn't an ndarray, it is treated as a scalar.
Chris@87 1070 c : array_like
Chris@87 1071 Array of coefficients ordered so that the coefficient of the term of
Chris@87 1072 multi-degree i,j is contained in `c[i,j]`. If `c` has dimension
Chris@87 1073 greater than two the remaining indices enumerate multiple sets of
Chris@87 1074 coefficients.
Chris@87 1075
Chris@87 1076 Returns
Chris@87 1077 -------
Chris@87 1078 values : ndarray, compatible object
Chris@87 1079 The values of the two dimensional Chebyshev series at points in the
Chris@87 1080 Cartesian product of `x` and `y`.
Chris@87 1081
Chris@87 1082 See Also
Chris@87 1083 --------
Chris@87 1084 legval, legval2d, legval3d, leggrid3d
Chris@87 1085
Chris@87 1086 Notes
Chris@87 1087 -----
Chris@87 1088
Chris@87 1089 .. versionadded::1.7.0
Chris@87 1090
Chris@87 1091 """
Chris@87 1092 c = legval(x, c)
Chris@87 1093 c = legval(y, c)
Chris@87 1094 return c
Chris@87 1095
Chris@87 1096
Chris@87 1097 def legval3d(x, y, z, c):
Chris@87 1098 """
Chris@87 1099 Evaluate a 3-D Legendre series at points (x, y, z).
Chris@87 1100
Chris@87 1101 This function returns the values:
Chris@87 1102
Chris@87 1103 .. math:: p(x,y,z) = \\sum_{i,j,k} c_{i,j,k} * L_i(x) * L_j(y) * L_k(z)
Chris@87 1104
Chris@87 1105 The parameters `x`, `y`, and `z` are converted to arrays only if
Chris@87 1106 they are tuples or a lists, otherwise they are treated as a scalars and
Chris@87 1107 they must have the same shape after conversion. In either case, either
Chris@87 1108 `x`, `y`, and `z` or their elements must support multiplication and
Chris@87 1109 addition both with themselves and with the elements of `c`.
Chris@87 1110
Chris@87 1111 If `c` has fewer than 3 dimensions, ones are implicitly appended to its
Chris@87 1112 shape to make it 3-D. The shape of the result will be c.shape[3:] +
Chris@87 1113 x.shape.
Chris@87 1114
Chris@87 1115 Parameters
Chris@87 1116 ----------
Chris@87 1117 x, y, z : array_like, compatible object
Chris@87 1118 The three dimensional series is evaluated at the points
Chris@87 1119 `(x, y, z)`, where `x`, `y`, and `z` must have the same shape. If
Chris@87 1120 any of `x`, `y`, or `z` is a list or tuple, it is first converted
Chris@87 1121 to an ndarray, otherwise it is left unchanged and if it isn't an
Chris@87 1122 ndarray it is treated as a scalar.
Chris@87 1123 c : array_like
Chris@87 1124 Array of coefficients ordered so that the coefficient of the term of
Chris@87 1125 multi-degree i,j,k is contained in ``c[i,j,k]``. If `c` has dimension
Chris@87 1126 greater than 3 the remaining indices enumerate multiple sets of
Chris@87 1127 coefficients.
Chris@87 1128
Chris@87 1129 Returns
Chris@87 1130 -------
Chris@87 1131 values : ndarray, compatible object
Chris@87 1132 The values of the multidimensional polynomial on points formed with
Chris@87 1133 triples of corresponding values from `x`, `y`, and `z`.
Chris@87 1134
Chris@87 1135 See Also
Chris@87 1136 --------
Chris@87 1137 legval, legval2d, leggrid2d, leggrid3d
Chris@87 1138
Chris@87 1139 Notes
Chris@87 1140 -----
Chris@87 1141
Chris@87 1142 .. versionadded::1.7.0
Chris@87 1143
Chris@87 1144 """
Chris@87 1145 try:
Chris@87 1146 x, y, z = np.array((x, y, z), copy=0)
Chris@87 1147 except:
Chris@87 1148 raise ValueError('x, y, z are incompatible')
Chris@87 1149
Chris@87 1150 c = legval(x, c)
Chris@87 1151 c = legval(y, c, tensor=False)
Chris@87 1152 c = legval(z, c, tensor=False)
Chris@87 1153 return c
Chris@87 1154
Chris@87 1155
Chris@87 1156 def leggrid3d(x, y, z, c):
Chris@87 1157 """
Chris@87 1158 Evaluate a 3-D Legendre series on the Cartesian product of x, y, and z.
Chris@87 1159
Chris@87 1160 This function returns the values:
Chris@87 1161
Chris@87 1162 .. math:: p(a,b,c) = \\sum_{i,j,k} c_{i,j,k} * L_i(a) * L_j(b) * L_k(c)
Chris@87 1163
Chris@87 1164 where the points `(a, b, c)` consist of all triples formed by taking
Chris@87 1165 `a` from `x`, `b` from `y`, and `c` from `z`. The resulting points form
Chris@87 1166 a grid with `x` in the first dimension, `y` in the second, and `z` in
Chris@87 1167 the third.
Chris@87 1168
Chris@87 1169 The parameters `x`, `y`, and `z` are converted to arrays only if they
Chris@87 1170 are tuples or a lists, otherwise they are treated as a scalars. In
Chris@87 1171 either case, either `x`, `y`, and `z` or their elements must support
Chris@87 1172 multiplication and addition both with themselves and with the elements
Chris@87 1173 of `c`.
Chris@87 1174
Chris@87 1175 If `c` has fewer than three dimensions, ones are implicitly appended to
Chris@87 1176 its shape to make it 3-D. The shape of the result will be c.shape[3:] +
Chris@87 1177 x.shape + y.shape + z.shape.
Chris@87 1178
Chris@87 1179 Parameters
Chris@87 1180 ----------
Chris@87 1181 x, y, z : array_like, compatible objects
Chris@87 1182 The three dimensional series is evaluated at the points in the
Chris@87 1183 Cartesian product of `x`, `y`, and `z`. If `x`,`y`, or `z` is a
Chris@87 1184 list or tuple, it is first converted to an ndarray, otherwise it is
Chris@87 1185 left unchanged and, if it isn't an ndarray, it is treated as a
Chris@87 1186 scalar.
Chris@87 1187 c : array_like
Chris@87 1188 Array of coefficients ordered so that the coefficients for terms of
Chris@87 1189 degree i,j are contained in ``c[i,j]``. If `c` has dimension
Chris@87 1190 greater than two the remaining indices enumerate multiple sets of
Chris@87 1191 coefficients.
Chris@87 1192
Chris@87 1193 Returns
Chris@87 1194 -------
Chris@87 1195 values : ndarray, compatible object
Chris@87 1196 The values of the two dimensional polynomial at points in the Cartesian
Chris@87 1197 product of `x` and `y`.
Chris@87 1198
Chris@87 1199 See Also
Chris@87 1200 --------
Chris@87 1201 legval, legval2d, leggrid2d, legval3d
Chris@87 1202
Chris@87 1203 Notes
Chris@87 1204 -----
Chris@87 1205
Chris@87 1206 .. versionadded::1.7.0
Chris@87 1207
Chris@87 1208 """
Chris@87 1209 c = legval(x, c)
Chris@87 1210 c = legval(y, c)
Chris@87 1211 c = legval(z, c)
Chris@87 1212 return c
Chris@87 1213
Chris@87 1214
Chris@87 1215 def legvander(x, deg):
Chris@87 1216 """Pseudo-Vandermonde matrix of given degree.
Chris@87 1217
Chris@87 1218 Returns the pseudo-Vandermonde matrix of degree `deg` and sample points
Chris@87 1219 `x`. The pseudo-Vandermonde matrix is defined by
Chris@87 1220
Chris@87 1221 .. math:: V[..., i] = L_i(x)
Chris@87 1222
Chris@87 1223 where `0 <= i <= deg`. The leading indices of `V` index the elements of
Chris@87 1224 `x` and the last index is the degree of the Legendre polynomial.
Chris@87 1225
Chris@87 1226 If `c` is a 1-D array of coefficients of length `n + 1` and `V` is the
Chris@87 1227 array ``V = legvander(x, n)``, then ``np.dot(V, c)`` and
Chris@87 1228 ``legval(x, c)`` are the same up to roundoff. This equivalence is
Chris@87 1229 useful both for least squares fitting and for the evaluation of a large
Chris@87 1230 number of Legendre series of the same degree and sample points.
Chris@87 1231
Chris@87 1232 Parameters
Chris@87 1233 ----------
Chris@87 1234 x : array_like
Chris@87 1235 Array of points. The dtype is converted to float64 or complex128
Chris@87 1236 depending on whether any of the elements are complex. If `x` is
Chris@87 1237 scalar it is converted to a 1-D array.
Chris@87 1238 deg : int
Chris@87 1239 Degree of the resulting matrix.
Chris@87 1240
Chris@87 1241 Returns
Chris@87 1242 -------
Chris@87 1243 vander : ndarray
Chris@87 1244 The pseudo-Vandermonde matrix. The shape of the returned matrix is
Chris@87 1245 ``x.shape + (deg + 1,)``, where The last index is the degree of the
Chris@87 1246 corresponding Legendre polynomial. The dtype will be the same as
Chris@87 1247 the converted `x`.
Chris@87 1248
Chris@87 1249 """
Chris@87 1250 ideg = int(deg)
Chris@87 1251 if ideg != deg:
Chris@87 1252 raise ValueError("deg must be integer")
Chris@87 1253 if ideg < 0:
Chris@87 1254 raise ValueError("deg must be non-negative")
Chris@87 1255
Chris@87 1256 x = np.array(x, copy=0, ndmin=1) + 0.0
Chris@87 1257 dims = (ideg + 1,) + x.shape
Chris@87 1258 dtyp = x.dtype
Chris@87 1259 v = np.empty(dims, dtype=dtyp)
Chris@87 1260 # Use forward recursion to generate the entries. This is not as accurate
Chris@87 1261 # as reverse recursion in this application but it is more efficient.
Chris@87 1262 v[0] = x*0 + 1
Chris@87 1263 if ideg > 0:
Chris@87 1264 v[1] = x
Chris@87 1265 for i in range(2, ideg + 1):
Chris@87 1266 v[i] = (v[i-1]*x*(2*i - 1) - v[i-2]*(i - 1))/i
Chris@87 1267 return np.rollaxis(v, 0, v.ndim)
Chris@87 1268
Chris@87 1269
Chris@87 1270 def legvander2d(x, y, deg):
Chris@87 1271 """Pseudo-Vandermonde matrix of given degrees.
Chris@87 1272
Chris@87 1273 Returns the pseudo-Vandermonde matrix of degrees `deg` and sample
Chris@87 1274 points `(x, y)`. The pseudo-Vandermonde matrix is defined by
Chris@87 1275
Chris@87 1276 .. math:: V[..., deg[1]*i + j] = L_i(x) * L_j(y),
Chris@87 1277
Chris@87 1278 where `0 <= i <= deg[0]` and `0 <= j <= deg[1]`. The leading indices of
Chris@87 1279 `V` index the points `(x, y)` and the last index encodes the degrees of
Chris@87 1280 the Legendre polynomials.
Chris@87 1281
Chris@87 1282 If ``V = legvander2d(x, y, [xdeg, ydeg])``, then the columns of `V`
Chris@87 1283 correspond to the elements of a 2-D coefficient array `c` of shape
Chris@87 1284 (xdeg + 1, ydeg + 1) in the order
Chris@87 1285
Chris@87 1286 .. math:: c_{00}, c_{01}, c_{02} ... , c_{10}, c_{11}, c_{12} ...
Chris@87 1287
Chris@87 1288 and ``np.dot(V, c.flat)`` and ``legval2d(x, y, c)`` will be the same
Chris@87 1289 up to roundoff. This equivalence is useful both for least squares
Chris@87 1290 fitting and for the evaluation of a large number of 2-D Legendre
Chris@87 1291 series of the same degrees and sample points.
Chris@87 1292
Chris@87 1293 Parameters
Chris@87 1294 ----------
Chris@87 1295 x, y : array_like
Chris@87 1296 Arrays of point coordinates, all of the same shape. The dtypes
Chris@87 1297 will be converted to either float64 or complex128 depending on
Chris@87 1298 whether any of the elements are complex. Scalars are converted to
Chris@87 1299 1-D arrays.
Chris@87 1300 deg : list of ints
Chris@87 1301 List of maximum degrees of the form [x_deg, y_deg].
Chris@87 1302
Chris@87 1303 Returns
Chris@87 1304 -------
Chris@87 1305 vander2d : ndarray
Chris@87 1306 The shape of the returned matrix is ``x.shape + (order,)``, where
Chris@87 1307 :math:`order = (deg[0]+1)*(deg([1]+1)`. The dtype will be the same
Chris@87 1308 as the converted `x` and `y`.
Chris@87 1309
Chris@87 1310 See Also
Chris@87 1311 --------
Chris@87 1312 legvander, legvander3d. legval2d, legval3d
Chris@87 1313
Chris@87 1314 Notes
Chris@87 1315 -----
Chris@87 1316
Chris@87 1317 .. versionadded::1.7.0
Chris@87 1318
Chris@87 1319 """
Chris@87 1320 ideg = [int(d) for d in deg]
Chris@87 1321 is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)]
Chris@87 1322 if is_valid != [1, 1]:
Chris@87 1323 raise ValueError("degrees must be non-negative integers")
Chris@87 1324 degx, degy = ideg
Chris@87 1325 x, y = np.array((x, y), copy=0) + 0.0
Chris@87 1326
Chris@87 1327 vx = legvander(x, degx)
Chris@87 1328 vy = legvander(y, degy)
Chris@87 1329 v = vx[..., None]*vy[..., None,:]
Chris@87 1330 return v.reshape(v.shape[:-2] + (-1,))
Chris@87 1331
Chris@87 1332
Chris@87 1333 def legvander3d(x, y, z, deg):
Chris@87 1334 """Pseudo-Vandermonde matrix of given degrees.
Chris@87 1335
Chris@87 1336 Returns the pseudo-Vandermonde matrix of degrees `deg` and sample
Chris@87 1337 points `(x, y, z)`. If `l, m, n` are the given degrees in `x, y, z`,
Chris@87 1338 then The pseudo-Vandermonde matrix is defined by
Chris@87 1339
Chris@87 1340 .. math:: V[..., (m+1)(n+1)i + (n+1)j + k] = L_i(x)*L_j(y)*L_k(z),
Chris@87 1341
Chris@87 1342 where `0 <= i <= l`, `0 <= j <= m`, and `0 <= j <= n`. The leading
Chris@87 1343 indices of `V` index the points `(x, y, z)` and the last index encodes
Chris@87 1344 the degrees of the Legendre polynomials.
Chris@87 1345
Chris@87 1346 If ``V = legvander3d(x, y, z, [xdeg, ydeg, zdeg])``, then the columns
Chris@87 1347 of `V` correspond to the elements of a 3-D coefficient array `c` of
Chris@87 1348 shape (xdeg + 1, ydeg + 1, zdeg + 1) in the order
Chris@87 1349
Chris@87 1350 .. math:: c_{000}, c_{001}, c_{002},... , c_{010}, c_{011}, c_{012},...
Chris@87 1351
Chris@87 1352 and ``np.dot(V, c.flat)`` and ``legval3d(x, y, z, c)`` will be the
Chris@87 1353 same up to roundoff. This equivalence is useful both for least squares
Chris@87 1354 fitting and for the evaluation of a large number of 3-D Legendre
Chris@87 1355 series of the same degrees and sample points.
Chris@87 1356
Chris@87 1357 Parameters
Chris@87 1358 ----------
Chris@87 1359 x, y, z : array_like
Chris@87 1360 Arrays of point coordinates, all of the same shape. The dtypes will
Chris@87 1361 be converted to either float64 or complex128 depending on whether
Chris@87 1362 any of the elements are complex. Scalars are converted to 1-D
Chris@87 1363 arrays.
Chris@87 1364 deg : list of ints
Chris@87 1365 List of maximum degrees of the form [x_deg, y_deg, z_deg].
Chris@87 1366
Chris@87 1367 Returns
Chris@87 1368 -------
Chris@87 1369 vander3d : ndarray
Chris@87 1370 The shape of the returned matrix is ``x.shape + (order,)``, where
Chris@87 1371 :math:`order = (deg[0]+1)*(deg([1]+1)*(deg[2]+1)`. The dtype will
Chris@87 1372 be the same as the converted `x`, `y`, and `z`.
Chris@87 1373
Chris@87 1374 See Also
Chris@87 1375 --------
Chris@87 1376 legvander, legvander3d. legval2d, legval3d
Chris@87 1377
Chris@87 1378 Notes
Chris@87 1379 -----
Chris@87 1380
Chris@87 1381 .. versionadded::1.7.0
Chris@87 1382
Chris@87 1383 """
Chris@87 1384 ideg = [int(d) for d in deg]
Chris@87 1385 is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)]
Chris@87 1386 if is_valid != [1, 1, 1]:
Chris@87 1387 raise ValueError("degrees must be non-negative integers")
Chris@87 1388 degx, degy, degz = ideg
Chris@87 1389 x, y, z = np.array((x, y, z), copy=0) + 0.0
Chris@87 1390
Chris@87 1391 vx = legvander(x, degx)
Chris@87 1392 vy = legvander(y, degy)
Chris@87 1393 vz = legvander(z, degz)
Chris@87 1394 v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:]
Chris@87 1395 return v.reshape(v.shape[:-3] + (-1,))
Chris@87 1396
Chris@87 1397
Chris@87 1398 def legfit(x, y, deg, rcond=None, full=False, w=None):
Chris@87 1399 """
Chris@87 1400 Least squares fit of Legendre series to data.
Chris@87 1401
Chris@87 1402 Return the coefficients of a Legendre series of degree `deg` that is the
Chris@87 1403 least squares fit to the data values `y` given at points `x`. If `y` is
Chris@87 1404 1-D the returned coefficients will also be 1-D. If `y` is 2-D multiple
Chris@87 1405 fits are done, one for each column of `y`, and the resulting
Chris@87 1406 coefficients are stored in the corresponding columns of a 2-D return.
Chris@87 1407 The fitted polynomial(s) are in the form
Chris@87 1408
Chris@87 1409 .. math:: p(x) = c_0 + c_1 * L_1(x) + ... + c_n * L_n(x),
Chris@87 1410
Chris@87 1411 where `n` is `deg`.
Chris@87 1412
Chris@87 1413 Parameters
Chris@87 1414 ----------
Chris@87 1415 x : array_like, shape (M,)
Chris@87 1416 x-coordinates of the M sample points ``(x[i], y[i])``.
Chris@87 1417 y : array_like, shape (M,) or (M, K)
Chris@87 1418 y-coordinates of the sample points. Several data sets of sample
Chris@87 1419 points sharing the same x-coordinates can be fitted at once by
Chris@87 1420 passing in a 2D-array that contains one dataset per column.
Chris@87 1421 deg : int
Chris@87 1422 Degree of the fitting polynomial
Chris@87 1423 rcond : float, optional
Chris@87 1424 Relative condition number of the fit. Singular values smaller than
Chris@87 1425 this relative to the largest singular value will be ignored. The
Chris@87 1426 default value is len(x)*eps, where eps is the relative precision of
Chris@87 1427 the float type, about 2e-16 in most cases.
Chris@87 1428 full : bool, optional
Chris@87 1429 Switch determining nature of return value. When it is False (the
Chris@87 1430 default) just the coefficients are returned, when True diagnostic
Chris@87 1431 information from the singular value decomposition is also returned.
Chris@87 1432 w : array_like, shape (`M`,), optional
Chris@87 1433 Weights. If not None, the contribution of each point
Chris@87 1434 ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the
Chris@87 1435 weights are chosen so that the errors of the products ``w[i]*y[i]``
Chris@87 1436 all have the same variance. The default value is None.
Chris@87 1437
Chris@87 1438 .. versionadded:: 1.5.0
Chris@87 1439
Chris@87 1440 Returns
Chris@87 1441 -------
Chris@87 1442 coef : ndarray, shape (M,) or (M, K)
Chris@87 1443 Legendre coefficients ordered from low to high. If `y` was 2-D,
Chris@87 1444 the coefficients for the data in column k of `y` are in column
Chris@87 1445 `k`.
Chris@87 1446
Chris@87 1447 [residuals, rank, singular_values, rcond] : list
Chris@87 1448 These values are only returned if `full` = True
Chris@87 1449
Chris@87 1450 resid -- sum of squared residuals of the least squares fit
Chris@87 1451 rank -- the numerical rank of the scaled Vandermonde matrix
Chris@87 1452 sv -- singular values of the scaled Vandermonde matrix
Chris@87 1453 rcond -- value of `rcond`.
Chris@87 1454
Chris@87 1455 For more details, see `linalg.lstsq`.
Chris@87 1456
Chris@87 1457 Warns
Chris@87 1458 -----
Chris@87 1459 RankWarning
Chris@87 1460 The rank of the coefficient matrix in the least-squares fit is
Chris@87 1461 deficient. The warning is only raised if `full` = False. The
Chris@87 1462 warnings can be turned off by
Chris@87 1463
Chris@87 1464 >>> import warnings
Chris@87 1465 >>> warnings.simplefilter('ignore', RankWarning)
Chris@87 1466
Chris@87 1467 See Also
Chris@87 1468 --------
Chris@87 1469 chebfit, polyfit, lagfit, hermfit, hermefit
Chris@87 1470 legval : Evaluates a Legendre series.
Chris@87 1471 legvander : Vandermonde matrix of Legendre series.
Chris@87 1472 legweight : Legendre weight function (= 1).
Chris@87 1473 linalg.lstsq : Computes a least-squares fit from the matrix.
Chris@87 1474 scipy.interpolate.UnivariateSpline : Computes spline fits.
Chris@87 1475
Chris@87 1476 Notes
Chris@87 1477 -----
Chris@87 1478 The solution is the coefficients of the Legendre series `p` that
Chris@87 1479 minimizes the sum of the weighted squared errors
Chris@87 1480
Chris@87 1481 .. math:: E = \\sum_j w_j^2 * |y_j - p(x_j)|^2,
Chris@87 1482
Chris@87 1483 where :math:`w_j` are the weights. This problem is solved by setting up
Chris@87 1484 as the (typically) overdetermined matrix equation
Chris@87 1485
Chris@87 1486 .. math:: V(x) * c = w * y,
Chris@87 1487
Chris@87 1488 where `V` is the weighted pseudo Vandermonde matrix of `x`, `c` are the
Chris@87 1489 coefficients to be solved for, `w` are the weights, and `y` are the
Chris@87 1490 observed values. This equation is then solved using the singular value
Chris@87 1491 decomposition of `V`.
Chris@87 1492
Chris@87 1493 If some of the singular values of `V` are so small that they are
Chris@87 1494 neglected, then a `RankWarning` will be issued. This means that the
Chris@87 1495 coefficient values may be poorly determined. Using a lower order fit
Chris@87 1496 will usually get rid of the warning. The `rcond` parameter can also be
Chris@87 1497 set to a value smaller than its default, but the resulting fit may be
Chris@87 1498 spurious and have large contributions from roundoff error.
Chris@87 1499
Chris@87 1500 Fits using Legendre series are usually better conditioned than fits
Chris@87 1501 using power series, but much can depend on the distribution of the
Chris@87 1502 sample points and the smoothness of the data. If the quality of the fit
Chris@87 1503 is inadequate splines may be a good alternative.
Chris@87 1504
Chris@87 1505 References
Chris@87 1506 ----------
Chris@87 1507 .. [1] Wikipedia, "Curve fitting",
Chris@87 1508 http://en.wikipedia.org/wiki/Curve_fitting
Chris@87 1509
Chris@87 1510 Examples
Chris@87 1511 --------
Chris@87 1512
Chris@87 1513 """
Chris@87 1514 order = int(deg) + 1
Chris@87 1515 x = np.asarray(x) + 0.0
Chris@87 1516 y = np.asarray(y) + 0.0
Chris@87 1517
Chris@87 1518 # check arguments.
Chris@87 1519 if deg < 0:
Chris@87 1520 raise ValueError("expected deg >= 0")
Chris@87 1521 if x.ndim != 1:
Chris@87 1522 raise TypeError("expected 1D vector for x")
Chris@87 1523 if x.size == 0:
Chris@87 1524 raise TypeError("expected non-empty vector for x")
Chris@87 1525 if y.ndim < 1 or y.ndim > 2:
Chris@87 1526 raise TypeError("expected 1D or 2D array for y")
Chris@87 1527 if len(x) != len(y):
Chris@87 1528 raise TypeError("expected x and y to have same length")
Chris@87 1529
Chris@87 1530 # set up the least squares matrices in transposed form
Chris@87 1531 lhs = legvander(x, deg).T
Chris@87 1532 rhs = y.T
Chris@87 1533 if w is not None:
Chris@87 1534 w = np.asarray(w) + 0.0
Chris@87 1535 if w.ndim != 1:
Chris@87 1536 raise TypeError("expected 1D vector for w")
Chris@87 1537 if len(x) != len(w):
Chris@87 1538 raise TypeError("expected x and w to have same length")
Chris@87 1539 # apply weights. Don't use inplace operations as they
Chris@87 1540 # can cause problems with NA.
Chris@87 1541 lhs = lhs * w
Chris@87 1542 rhs = rhs * w
Chris@87 1543
Chris@87 1544 # set rcond
Chris@87 1545 if rcond is None:
Chris@87 1546 rcond = len(x)*np.finfo(x.dtype).eps
Chris@87 1547
Chris@87 1548 # Determine the norms of the design matrix columns.
Chris@87 1549 if issubclass(lhs.dtype.type, np.complexfloating):
Chris@87 1550 scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1))
Chris@87 1551 else:
Chris@87 1552 scl = np.sqrt(np.square(lhs).sum(1))
Chris@87 1553 scl[scl == 0] = 1
Chris@87 1554
Chris@87 1555 # Solve the least squares problem.
Chris@87 1556 c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond)
Chris@87 1557 c = (c.T/scl).T
Chris@87 1558
Chris@87 1559 # warn on rank reduction
Chris@87 1560 if rank != order and not full:
Chris@87 1561 msg = "The fit may be poorly conditioned"
Chris@87 1562 warnings.warn(msg, pu.RankWarning)
Chris@87 1563
Chris@87 1564 if full:
Chris@87 1565 return c, [resids, rank, s, rcond]
Chris@87 1566 else:
Chris@87 1567 return c
Chris@87 1568
Chris@87 1569
Chris@87 1570 def legcompanion(c):
Chris@87 1571 """Return the scaled companion matrix of c.
Chris@87 1572
Chris@87 1573 The basis polynomials are scaled so that the companion matrix is
Chris@87 1574 symmetric when `c` is an Legendre basis polynomial. This provides
Chris@87 1575 better eigenvalue estimates than the unscaled case and for basis
Chris@87 1576 polynomials the eigenvalues are guaranteed to be real if
Chris@87 1577 `numpy.linalg.eigvalsh` is used to obtain them.
Chris@87 1578
Chris@87 1579 Parameters
Chris@87 1580 ----------
Chris@87 1581 c : array_like
Chris@87 1582 1-D array of Legendre series coefficients ordered from low to high
Chris@87 1583 degree.
Chris@87 1584
Chris@87 1585 Returns
Chris@87 1586 -------
Chris@87 1587 mat : ndarray
Chris@87 1588 Scaled companion matrix of dimensions (deg, deg).
Chris@87 1589
Chris@87 1590 Notes
Chris@87 1591 -----
Chris@87 1592
Chris@87 1593 .. versionadded::1.7.0
Chris@87 1594
Chris@87 1595 """
Chris@87 1596 # c is a trimmed copy
Chris@87 1597 [c] = pu.as_series([c])
Chris@87 1598 if len(c) < 2:
Chris@87 1599 raise ValueError('Series must have maximum degree of at least 1.')
Chris@87 1600 if len(c) == 2:
Chris@87 1601 return np.array([[-c[0]/c[1]]])
Chris@87 1602
Chris@87 1603 n = len(c) - 1
Chris@87 1604 mat = np.zeros((n, n), dtype=c.dtype)
Chris@87 1605 scl = 1./np.sqrt(2*np.arange(n) + 1)
Chris@87 1606 top = mat.reshape(-1)[1::n+1]
Chris@87 1607 bot = mat.reshape(-1)[n::n+1]
Chris@87 1608 top[...] = np.arange(1, n)*scl[:n-1]*scl[1:n]
Chris@87 1609 bot[...] = top
Chris@87 1610 mat[:, -1] -= (c[:-1]/c[-1])*(scl/scl[-1])*(n/(2*n - 1))
Chris@87 1611 return mat
Chris@87 1612
Chris@87 1613
Chris@87 1614 def legroots(c):
Chris@87 1615 """
Chris@87 1616 Compute the roots of a Legendre series.
Chris@87 1617
Chris@87 1618 Return the roots (a.k.a. "zeros") of the polynomial
Chris@87 1619
Chris@87 1620 .. math:: p(x) = \\sum_i c[i] * L_i(x).
Chris@87 1621
Chris@87 1622 Parameters
Chris@87 1623 ----------
Chris@87 1624 c : 1-D array_like
Chris@87 1625 1-D array of coefficients.
Chris@87 1626
Chris@87 1627 Returns
Chris@87 1628 -------
Chris@87 1629 out : ndarray
Chris@87 1630 Array of the roots of the series. If all the roots are real,
Chris@87 1631 then `out` is also real, otherwise it is complex.
Chris@87 1632
Chris@87 1633 See Also
Chris@87 1634 --------
Chris@87 1635 polyroots, chebroots, lagroots, hermroots, hermeroots
Chris@87 1636
Chris@87 1637 Notes
Chris@87 1638 -----
Chris@87 1639 The root estimates are obtained as the eigenvalues of the companion
Chris@87 1640 matrix, Roots far from the origin of the complex plane may have large
Chris@87 1641 errors due to the numerical instability of the series for such values.
Chris@87 1642 Roots with multiplicity greater than 1 will also show larger errors as
Chris@87 1643 the value of the series near such points is relatively insensitive to
Chris@87 1644 errors in the roots. Isolated roots near the origin can be improved by
Chris@87 1645 a few iterations of Newton's method.
Chris@87 1646
Chris@87 1647 The Legendre series basis polynomials aren't powers of ``x`` so the
Chris@87 1648 results of this function may seem unintuitive.
Chris@87 1649
Chris@87 1650 Examples
Chris@87 1651 --------
Chris@87 1652 >>> import numpy.polynomial.legendre as leg
Chris@87 1653 >>> leg.legroots((1, 2, 3, 4)) # 4L_3 + 3L_2 + 2L_1 + 1L_0, all real roots
Chris@87 1654 array([-0.85099543, -0.11407192, 0.51506735])
Chris@87 1655
Chris@87 1656 """
Chris@87 1657 # c is a trimmed copy
Chris@87 1658 [c] = pu.as_series([c])
Chris@87 1659 if len(c) < 2:
Chris@87 1660 return np.array([], dtype=c.dtype)
Chris@87 1661 if len(c) == 2:
Chris@87 1662 return np.array([-c[0]/c[1]])
Chris@87 1663
Chris@87 1664 m = legcompanion(c)
Chris@87 1665 r = la.eigvals(m)
Chris@87 1666 r.sort()
Chris@87 1667 return r
Chris@87 1668
Chris@87 1669
Chris@87 1670 def leggauss(deg):
Chris@87 1671 """
Chris@87 1672 Gauss-Legendre quadrature.
Chris@87 1673
Chris@87 1674 Computes the sample points and weights for Gauss-Legendre quadrature.
Chris@87 1675 These sample points and weights will correctly integrate polynomials of
Chris@87 1676 degree :math:`2*deg - 1` or less over the interval :math:`[-1, 1]` with
Chris@87 1677 the weight function :math:`f(x) = 1`.
Chris@87 1678
Chris@87 1679 Parameters
Chris@87 1680 ----------
Chris@87 1681 deg : int
Chris@87 1682 Number of sample points and weights. It must be >= 1.
Chris@87 1683
Chris@87 1684 Returns
Chris@87 1685 -------
Chris@87 1686 x : ndarray
Chris@87 1687 1-D ndarray containing the sample points.
Chris@87 1688 y : ndarray
Chris@87 1689 1-D ndarray containing the weights.
Chris@87 1690
Chris@87 1691 Notes
Chris@87 1692 -----
Chris@87 1693
Chris@87 1694 .. versionadded::1.7.0
Chris@87 1695
Chris@87 1696 The results have only been tested up to degree 100, higher degrees may
Chris@87 1697 be problematic. The weights are determined by using the fact that
Chris@87 1698
Chris@87 1699 .. math:: w_k = c / (L'_n(x_k) * L_{n-1}(x_k))
Chris@87 1700
Chris@87 1701 where :math:`c` is a constant independent of :math:`k` and :math:`x_k`
Chris@87 1702 is the k'th root of :math:`L_n`, and then scaling the results to get
Chris@87 1703 the right value when integrating 1.
Chris@87 1704
Chris@87 1705 """
Chris@87 1706 ideg = int(deg)
Chris@87 1707 if ideg != deg or ideg < 1:
Chris@87 1708 raise ValueError("deg must be a non-negative integer")
Chris@87 1709
Chris@87 1710 # first approximation of roots. We use the fact that the companion
Chris@87 1711 # matrix is symmetric in this case in order to obtain better zeros.
Chris@87 1712 c = np.array([0]*deg + [1])
Chris@87 1713 m = legcompanion(c)
Chris@87 1714 x = la.eigvals(m)
Chris@87 1715 x.sort()
Chris@87 1716
Chris@87 1717 # improve roots by one application of Newton
Chris@87 1718 dy = legval(x, c)
Chris@87 1719 df = legval(x, legder(c))
Chris@87 1720 x -= dy/df
Chris@87 1721
Chris@87 1722 # compute the weights. We scale the factor to avoid possible numerical
Chris@87 1723 # overflow.
Chris@87 1724 fm = legval(x, c[1:])
Chris@87 1725 fm /= np.abs(fm).max()
Chris@87 1726 df /= np.abs(df).max()
Chris@87 1727 w = 1/(fm * df)
Chris@87 1728
Chris@87 1729 # for Legendre we can also symmetrize
Chris@87 1730 w = (w + w[::-1])/2
Chris@87 1731 x = (x - x[::-1])/2
Chris@87 1732
Chris@87 1733 # scale w to get the right value
Chris@87 1734 w *= 2. / w.sum()
Chris@87 1735
Chris@87 1736 return x, w
Chris@87 1737
Chris@87 1738
Chris@87 1739 def legweight(x):
Chris@87 1740 """
Chris@87 1741 Weight function of the Legendre polynomials.
Chris@87 1742
Chris@87 1743 The weight function is :math:`1` and the interval of integration is
Chris@87 1744 :math:`[-1, 1]`. The Legendre polynomials are orthogonal, but not
Chris@87 1745 normalized, with respect to this weight function.
Chris@87 1746
Chris@87 1747 Parameters
Chris@87 1748 ----------
Chris@87 1749 x : array_like
Chris@87 1750 Values at which the weight function will be computed.
Chris@87 1751
Chris@87 1752 Returns
Chris@87 1753 -------
Chris@87 1754 w : ndarray
Chris@87 1755 The weight function at `x`.
Chris@87 1756
Chris@87 1757 Notes
Chris@87 1758 -----
Chris@87 1759
Chris@87 1760 .. versionadded::1.7.0
Chris@87 1761
Chris@87 1762 """
Chris@87 1763 w = x*0.0 + 1.0
Chris@87 1764 return w
Chris@87 1765
Chris@87 1766 #
Chris@87 1767 # Legendre series class
Chris@87 1768 #
Chris@87 1769
Chris@87 1770 class Legendre(ABCPolyBase):
Chris@87 1771 """A Legendre series class.
Chris@87 1772
Chris@87 1773 The Legendre class provides the standard Python numerical methods
Chris@87 1774 '+', '-', '*', '//', '%', 'divmod', '**', and '()' as well as the
Chris@87 1775 attributes and methods listed in the `ABCPolyBase` documentation.
Chris@87 1776
Chris@87 1777 Parameters
Chris@87 1778 ----------
Chris@87 1779 coef : array_like
Chris@87 1780 Legendre coefficients in order of increasing degree, i.e.,
Chris@87 1781 ``(1, 2, 3)`` gives ``1*P_0(x) + 2*P_1(x) + 3*P_2(x)``.
Chris@87 1782 domain : (2,) array_like, optional
Chris@87 1783 Domain to use. The interval ``[domain[0], domain[1]]`` is mapped
Chris@87 1784 to the interval ``[window[0], window[1]]`` by shifting and scaling.
Chris@87 1785 The default value is [-1, 1].
Chris@87 1786 window : (2,) array_like, optional
Chris@87 1787 Window, see `domain` for its use. The default value is [-1, 1].
Chris@87 1788
Chris@87 1789 .. versionadded:: 1.6.0
Chris@87 1790
Chris@87 1791 """
Chris@87 1792 # Virtual Functions
Chris@87 1793 _add = staticmethod(legadd)
Chris@87 1794 _sub = staticmethod(legsub)
Chris@87 1795 _mul = staticmethod(legmul)
Chris@87 1796 _div = staticmethod(legdiv)
Chris@87 1797 _pow = staticmethod(legpow)
Chris@87 1798 _val = staticmethod(legval)
Chris@87 1799 _int = staticmethod(legint)
Chris@87 1800 _der = staticmethod(legder)
Chris@87 1801 _fit = staticmethod(legfit)
Chris@87 1802 _line = staticmethod(legline)
Chris@87 1803 _roots = staticmethod(legroots)
Chris@87 1804 _fromroots = staticmethod(legfromroots)
Chris@87 1805
Chris@87 1806 # Virtual properties
Chris@87 1807 nickname = 'leg'
Chris@87 1808 domain = np.array(legdomain)
Chris@87 1809 window = np.array(legdomain)