1"""

2==================================================

3 Legendre Series (:mod:`numpy.polynomial.legendre`)

4==================================================

5

6 This module provides a number of objects (mostly functions) useful for

7 dealing with Legendre series, including a `Legendre` class that

8 encapsulates the usual arithmetic operations. (General information

9 on how this module represents and works with such polynomials is in the

10 docstring for its "parent" sub-package, `numpy.polynomial`).

11

12 Classes

13-------

14.. autosummary::

15 :toctree: generated/

16

17 Legendre

18

19 Constants

20---------

21

22.. autosummary::

23 :toctree: generated/

24

25 legdomain

26 legzero

27 legone

28 legx

29

30 Arithmetic

31----------

32

33.. autosummary::

34 :toctree: generated/

35

36 legadd

37 legsub

38 legmulx

39 legmul

40 legdiv

41 legpow

42 legval

43 legval2d

44 legval3d

45 leggrid2d

46 leggrid3d

47

48 Calculus

49--------

50

51.. autosummary::

52 :toctree: generated/

53

54 legder

55 legint

56

57 Misc Functions

58--------------

59

60.. autosummary::

61 :toctree: generated/

62

63 legfromroots

64 legroots

65 legvander

66 legvander2d

67 legvander3d

68 leggauss

69 legweight

70 legcompanion

71 legfit

72 legtrim

73 legline

74 leg2poly

75 poly2leg

76

77 See also

78--------

79 numpy.polynomial

80

81"""

82 import numpy as np

83 import numpy.linalg as la

84 from numpy.core.multiarray import normalize_axis_index

85

86 from . import polyutils as pu

87 from ._polybase import ABCPolyBase

88

89__all__ = [

90 'legzero', 'legone', 'legx', 'legdomain', 'legline', 'legadd',

91 'legsub', 'legmulx', 'legmul', 'legdiv', 'legpow', 'legval', 'legder',

92 'legint', 'leg2poly', 'poly2leg', 'legfromroots', 'legvander',

93 'legfit', 'legtrim', 'legroots', 'Legendre', 'legval2d', 'legval3d',

94 'leggrid2d', 'leggrid3d', 'legvander2d', 'legvander3d', 'legcompanion',

95 'leggauss', 'legweight']

96

97 legtrim = pu.trimcoef

98

99

100 def poly2leg(pol):

101 """

102 Convert a polynomial to a Legendre series.

103

104 Convert an array representing the coefficients of a polynomial (relative

105 to the "standard" basis) ordered from lowest degree to highest, to an

106 array of the coefficients of the equivalent Legendre series, ordered

107 from lowest to highest degree.

108

109 Parameters

110 ----------

111 pol : array_like

112 1-D array containing the polynomial coefficients

113

114 Returns

115 -------

116 c : ndarray

117 1-D array containing the coefficients of the equivalent Legendre

118 series.

119

120 See Also

121 --------

122 leg2poly

123

124 Notes

125 -----

126 The easy way to do conversions between polynomial basis sets

127 is to use the convert method of a class instance.

128

129 Examples

130 --------

131 >>> from numpy import polynomial as P

132 >>> p = P.Polynomial(np.arange(4))

133 >>> p

134 Polynomial([0., 1., 2., 3.], domain=[-1, 1], window=[-1, 1])

135 >>> c = P.Legendre(P.legendre.poly2leg(p.coef))

136 >>> c

137 Legendre([ 1. , 3.25, 1. , 0.75], domain=[-1, 1], window=[-1, 1]) # may vary

138

139 """

140 [pol] = pu.as_series([pol])

141 deg = len(pol) - 1

142 res = 0

143 for i in range(deg, -1, -1):

144 res = legadd(legmulx(res), pol[i])

145 return res

146

147

148 def leg2poly(c):

149 """

150 Convert a Legendre series to a polynomial.

151

152 Convert an array representing the coefficients of a Legendre series,

153 ordered from lowest degree to highest, to an array of the coefficients

154 of the equivalent polynomial (relative to the "standard" basis) ordered

155 from lowest to highest degree.

156

157 Parameters

158 ----------

159 c : array_like

160 1-D array containing the Legendre series coefficients, ordered

161 from lowest order term to highest.

162

163 Returns

164 -------

165 pol : ndarray

166 1-D array containing the coefficients of the equivalent polynomial

167 (relative to the "standard" basis) ordered from lowest order term

168 to highest.

169

170 See Also

171 --------

172 poly2leg

173

174 Notes

175 -----

176 The easy way to do conversions between polynomial basis sets

177 is to use the convert method of a class instance.

178

179 Examples

180 --------

181 >>> from numpy import polynomial as P

182 >>> c = P.Legendre(range(4))

183 >>> c

184 Legendre([0., 1., 2., 3.], domain=[-1, 1], window=[-1, 1])

185 >>> p = c.convert(kind=P.Polynomial)

186 >>> p

187 Polynomial([-1. , -3.5, 3. , 7.5], domain=[-1., 1.], window=[-1., 1.])

188 >>> P.legendre.leg2poly(range(4))

189 array([-1. , -3.5, 3. , 7.5])

190

191

192 """

193 from .polynomial import polyadd, polysub, polymulx

194

195 [c] = pu.as_series([c])

196 n = len(c)

197 if n < 3:

198 return c

199 else:

200 c0 = c[-2]

201 c1 = c[-1]

202 # i is the current degree of c1

203 for i in range(n - 1, 1, -1):

204 tmp = c0

205 c0 = polysub(c[i - 2], (c1*(i - 1))/i)

206 c1 = polyadd(tmp, (polymulx(c1)*(2*i - 1))/i)

207 return polyadd(c0, polymulx(c1))

208

209#

210# These are constant arrays are of integer type so as to be compatible

211# with the widest range of other types, such as Decimal.

212#

213

214# Legendre

215 legdomain = np.array([-1, 1])

216

217# Legendre coefficients representing zero.

218 legzero = np.array([0])

219

220# Legendre coefficients representing one.

221 legone = np.array([1])

222

223# Legendre coefficients representing the identity x.

224 legx = np.array([0, 1])

225

226

227 def legline(off, scl):

228 """

229 Legendre series whose graph is a straight line.

230

231

232

233 Parameters

234 ----------

235 off, scl : scalars

236 The specified line is given by ``off + scl*x``.

237

238 Returns

239 -------

240 y : ndarray

241 This module's representation of the Legendre series for

242 ``off + scl*x``.

243

244 See Also

245 --------

246 numpy.polynomial.polynomial.polyline

247 numpy.polynomial.chebyshev.chebline

248 numpy.polynomial.laguerre.lagline

249 numpy.polynomial.hermite.hermline

250 numpy.polynomial.hermite_e.hermeline

251

252 Examples

253 --------

254 >>> import numpy.polynomial.legendre as L

255 >>> L.legline(3,2)

256 array([3, 2])

257 >>> L.legval(-3, L.legline(3,2)) # should be -3

258 -3.0

259

260 """

261 if scl != 0:

262 return np.array([off, scl])

263 else:

264 return np.array([off])

265

266

267 def legfromroots(roots):

268 """

269 Generate a Legendre series with given roots.

270

271 The function returns the coefficients of the polynomial

272

273 .. math:: p(x) = (x - r_0) * (x - r_1) * ... * (x - r_n),

274

275 in Legendre form, where the `r_n` are the roots specified in `roots`.

276 If a zero has multiplicity n, then it must appear in `roots` n times.

277 For instance, if 2 is a root of multiplicity three and 3 is a root of

278 multiplicity 2, then `roots` looks something like [2, 2, 2, 3, 3]. The

279 roots can appear in any order.

280

281 If the returned coefficients are `c`, then

282

283 .. math:: p(x) = c_0 + c_1 * L_1(x) + ... + c_n * L_n(x)

284

285 The coefficient of the last term is not generally 1 for monic

286 polynomials in Legendre form.

287

288 Parameters

289 ----------

290 roots : array_like

291 Sequence containing the roots.

292

293 Returns

294 -------

295 out : ndarray

296 1-D array of coefficients. If all roots are real then `out` is a

297 real array, if some of the roots are complex, then `out` is complex

298 even if all the coefficients in the result are real (see Examples

299 below).

300

301 See Also

302 --------

303 numpy.polynomial.polynomial.polyfromroots

304 numpy.polynomial.chebyshev.chebfromroots

305 numpy.polynomial.laguerre.lagfromroots

306 numpy.polynomial.hermite.hermfromroots

307 numpy.polynomial.hermite_e.hermefromroots

308

309 Examples

310 --------

311 >>> import numpy.polynomial.legendre as L

312 >>> L.legfromroots((-1,0,1)) # x^3 - x relative to the standard basis

313 array([ 0. , -0.4, 0. , 0.4])

314 >>> j = complex(0,1)

315 >>> L.legfromroots((-j,j)) # x^2 + 1 relative to the standard basis

316 array([ 1.33333333+0.j, 0.00000000+0.j, 0.66666667+0.j]) # may vary

317

318 """

319 return pu._fromroots(legline, legmul, roots)

320

321

322 def legadd(c1, c2):

323 """

324 Add one Legendre series to another.

325

326 Returns the sum of two Legendre series `c1` + `c2`. The arguments

327 are sequences of coefficients ordered from lowest order term to

328 highest, i.e., [1,2,3] represents the series ``P_0 + 2*P_1 + 3*P_2``.

329

330 Parameters

331 ----------

332 c1, c2 : array_like

333 1-D arrays of Legendre series coefficients ordered from low to

334 high.

335

336 Returns

337 -------

338 out : ndarray

339 Array representing the Legendre series of their sum.

340

341 See Also

342 --------

343 legsub, legmulx, legmul, legdiv, legpow

344

345 Notes

346 -----

347 Unlike multiplication, division, etc., the sum of two Legendre series

348 is a Legendre series (without having to "reproject" the result onto

349 the basis set) so addition, just like that of "standard" polynomials,

350 is simply "component-wise."

351

352 Examples

353 --------

354 >>> from numpy.polynomial import legendre as L

355 >>> c1 = (1,2,3)

356 >>> c2 = (3,2,1)

357 >>> L.legadd(c1,c2)

358 array([4., 4., 4.])

359

360 """

361 return pu._add(c1, c2)

362

363

364 def legsub(c1, c2):

365 """

366 Subtract one Legendre series from another.

367

368 Returns the difference of two Legendre series `c1` - `c2`. The

369 sequences of coefficients are from lowest order term to highest, i.e.,

370 [1,2,3] represents the series ``P_0 + 2*P_1 + 3*P_2``.

371

372 Parameters

373 ----------

374 c1, c2 : array_like

375 1-D arrays of Legendre series coefficients ordered from low to

376 high.

377

378 Returns

379 -------

380 out : ndarray

381 Of Legendre series coefficients representing their difference.

382

383 See Also

384 --------

385 legadd, legmulx, legmul, legdiv, legpow

386

387 Notes

388 -----

389 Unlike multiplication, division, etc., the difference of two Legendre

390 series is a Legendre series (without having to "reproject" the result

391 onto the basis set) so subtraction, just like that of "standard"

392 polynomials, is simply "component-wise."

393

394 Examples

395 --------

396 >>> from numpy.polynomial import legendre as L

397 >>> c1 = (1,2,3)

398 >>> c2 = (3,2,1)

399 >>> L.legsub(c1,c2)

400 array([-2., 0., 2.])

401 >>> L.legsub(c2,c1) # -C.legsub(c1,c2)

402 array([ 2., 0., -2.])

403

404 """

405 return pu._sub(c1, c2)

406

407

408 def legmulx(c):

409 """Multiply a Legendre series by x.

410

411 Multiply the Legendre series `c` by x, where x is the independent

412 variable.

413

414

415 Parameters

416 ----------

417 c : array_like

418 1-D array of Legendre series coefficients ordered from low to

419 high.

420

421 Returns

422 -------

423 out : ndarray

424 Array representing the result of the multiplication.

425

426 See Also

427 --------

428 legadd, legmul, legdiv, legpow

429

430 Notes

431 -----

432 The multiplication uses the recursion relationship for Legendre

433 polynomials in the form

434

435 .. math::

436

437 xP_i(x) = ((i + 1)*P_{i + 1}(x) + i*P_{i - 1}(x))/(2i + 1)

438

439 Examples

440 --------

441 >>> from numpy.polynomial import legendre as L

442 >>> L.legmulx([1,2,3])

443 array([ 0.66666667, 2.2, 1.33333333, 1.8]) # may vary

444

445 """

446 # c is a trimmed copy

447 [c] = pu.as_series([c])

448 # The zero series needs special treatment

449 if len(c) == 1 and c[0] == 0:

450 return c

451

452 prd = np.empty(len(c) + 1, dtype=c.dtype)

453 prd[0] = c[0]*0

454 prd[1] = c[0]

455 for i in range(1, len(c)):

456 j = i + 1

457 k = i - 1

458 s = i + j

459 prd[j] = (c[i]*j)/s

460 prd[k] += (c[i]*i)/s

461 return prd

462

463

464 def legmul(c1, c2):

465 """

466 Multiply one Legendre series by another.

467

468 Returns the product of two Legendre series `c1` * `c2`. The arguments

469 are sequences of coefficients, from lowest order "term" to highest,

470 e.g., [1,2,3] represents the series ``P_0 + 2*P_1 + 3*P_2``.

471

472 Parameters

473 ----------

474 c1, c2 : array_like

475 1-D arrays of Legendre series coefficients ordered from low to

476 high.

477

478 Returns

479 -------

480 out : ndarray

481 Of Legendre series coefficients representing their product.

482

483 See Also

484 --------

485 legadd, legsub, legmulx, legdiv, legpow

486

487 Notes

488 -----

489 In general, the (polynomial) product of two C-series results in terms

490 that are not in the Legendre polynomial basis set. Thus, to express

491 the product as a Legendre series, it is necessary to "reproject" the

492 product onto said basis set, which may produce "unintuitive" (but

493 correct) results; see Examples section below.

494

495 Examples

496 --------

497 >>> from numpy.polynomial import legendre as L

498 >>> c1 = (1,2,3)

499 >>> c2 = (3,2)

500 >>> L.legmul(c1,c2) # multiplication requires "reprojection"

501 array([ 4.33333333, 10.4 , 11.66666667, 3.6 ]) # may vary

502

503 """

504 # s1, s2 are trimmed copies

505 [c1, c2] = pu.as_series([c1, c2])

506

507 if len(c1) > len(c2):

508 c = c2

509 xs = c1

510 else:

511 c = c1

512 xs = c2

513

514 if len(c) == 1:

515 c0 = c[0]*xs

516 c1 = 0

517 elif len(c) == 2:

518 c0 = c[0]*xs

519 c1 = c[1]*xs

520 else:

521 nd = len(c)

522 c0 = c[-2]*xs

523 c1 = c[-1]*xs

524 for i in range(3, len(c) + 1):

525 tmp = c0

526 nd = nd - 1

527 c0 = legsub(c[-i]*xs, (c1*(nd - 1))/nd)

528 c1 = legadd(tmp, (legmulx(c1)*(2*nd - 1))/nd)

529 return legadd(c0, legmulx(c1))

530

531

532 def legdiv(c1, c2):

533 """

534 Divide one Legendre series by another.

535

536 Returns the quotient-with-remainder of two Legendre series

537 `c1` / `c2`. The arguments are sequences of coefficients from lowest

538 order "term" to highest, e.g., [1,2,3] represents the series

539 ``P_0 + 2*P_1 + 3*P_2``.

540

541 Parameters

542 ----------

543 c1, c2 : array_like

544 1-D arrays of Legendre series coefficients ordered from low to

545 high.

546

547 Returns

548 -------

549 quo, rem : ndarrays

550 Of Legendre series coefficients representing the quotient and

551 remainder.

552

553 See Also

554 --------

555 legadd, legsub, legmulx, legmul, legpow

556

557 Notes

558 -----

559 In general, the (polynomial) division of one Legendre series by another

560 results in quotient and remainder terms that are not in the Legendre

561 polynomial basis set. Thus, to express these results as a Legendre

562 series, it is necessary to "reproject" the results onto the Legendre

563 basis set, which may produce "unintuitive" (but correct) results; see

564 Examples section below.

565

566 Examples

567 --------

568 >>> from numpy.polynomial import legendre as L

569 >>> c1 = (1,2,3)

570 >>> c2 = (3,2,1)

571 >>> L.legdiv(c1,c2) # quotient "intuitive," remainder not

572 (array([3.]), array([-8., -4.]))

573 >>> c2 = (0,1,2,3)

574 >>> L.legdiv(c2,c1) # neither "intuitive"

575 (array([-0.07407407, 1.66666667]), array([-1.03703704, -2.51851852])) # may vary

576

577 """

578 return pu._div(legmul, c1, c2)

579

580

581 def legpow(c, pow, maxpower=16):

582 """Raise a Legendre series to a power.

583

584 Returns the Legendre series `c` raised to the power `pow`. The

585 argument `c` is a sequence of coefficients ordered from low to high.

586 i.e., [1,2,3] is the series ``P_0 + 2*P_1 + 3*P_2.``

587

588 Parameters

589 ----------

590 c : array_like

591 1-D array of Legendre series coefficients ordered from low to

592 high.

593 pow : integer

594 Power to which the series will be raised

595 maxpower : integer, optional

596 Maximum power allowed. This is mainly to limit growth of the series

597 to unmanageable size. Default is 16

598

599 Returns

600 -------

601 coef : ndarray

602 Legendre series of power.

603

604 See Also

605 --------

606 legadd, legsub, legmulx, legmul, legdiv

607

608 """

609 return pu._pow(legmul, c, pow, maxpower)

610

611

612 def legder(c, m=1, scl=1, axis=0):

613 """

614 Differentiate a Legendre series.

615

616 Returns the Legendre series coefficients `c` differentiated `m` times

617 along `axis`. At each iteration the result is multiplied by `scl` (the

618 scaling factor is for use in a linear change of variable). The argument

619 `c` is an array of coefficients from low to high degree along each

620 axis, e.g., [1,2,3] represents the series ``1*L_0 + 2*L_1 + 3*L_2``

621 while [[1,2],[1,2]] represents ``1*L_0(x)*L_0(y) + 1*L_1(x)*L_0(y) +

622 2*L_0(x)*L_1(y) + 2*L_1(x)*L_1(y)`` if axis=0 is ``x`` and axis=1 is

623 ``y``.

624

625 Parameters

626 ----------

627 c : array_like

628 Array of Legendre series coefficients. If c is multidimensional the

629 different axis correspond to different variables with the degree in

630 each axis given by the corresponding index.

631 m : int, optional

632 Number of derivatives taken, must be non-negative. (Default: 1)

633 scl : scalar, optional

634 Each differentiation is multiplied by `scl`. The end result is

635 multiplication by ``scl**m``. This is for use in a linear change of

636 variable. (Default: 1)

637 axis : int, optional

638 Axis over which the derivative is taken. (Default: 0).

639

640 .. versionadded:: 1.7.0

641

642 Returns

643 -------

644 der : ndarray

645 Legendre series of the derivative.

646

647 See Also

648 --------

649 legint

650

651 Notes

652 -----

653 In general, the result of differentiating a Legendre series does not

654 resemble the same operation on a power series. Thus the result of this

655 function may be "unintuitive," albeit correct; see Examples section

656 below.

657

658 Examples

659 --------

660 >>> from numpy.polynomial import legendre as L

661 >>> c = (1,2,3,4)

662 >>> L.legder(c)

663 array([ 6., 9., 20.])

664 >>> L.legder(c, 3)

665 array([60.])

666 >>> L.legder(c, scl=-1)

667 array([ -6., -9., -20.])

668 >>> L.legder(c, 2,-1)

669 array([ 9., 60.])

670

671 """

672 c = np.array(c, ndmin=1, copy=True)

673 if c.dtype.char in '?bBhHiIlLqQpP':

674 c = c.astype(np.double)

675 cnt = pu._deprecate_as_int(m, "the order of derivation")

676 iaxis = pu._deprecate_as_int(axis, "the axis")

677 if cnt < 0:

678 raise ValueError("The order of derivation must be non-negative")

679 iaxis = normalize_axis_index(iaxis, c.ndim)

680

681 if cnt == 0:

682 return c

683

684 c = np.moveaxis(c, iaxis, 0)

685 n = len(c)

686 if cnt >= n:

687 c = c[:1]*0

688 else:

689 for i in range(cnt):

690 n = n - 1

691 c *= scl

692 der = np.empty((n,) + c.shape[1:], dtype=c.dtype)

693 for j in range(n, 2, -1):

694 der[j - 1] = (2*j - 1)*c[j]

695 c[j - 2] += c[j]

696 if n > 1:

697 der[1] = 3*c[2]

698 der[0] = c[1]

699 c = der

700 c = np.moveaxis(c, 0, iaxis)

701 return c

702

703

704 def legint(c, m=1, k=[], lbnd=0, scl=1, axis=0):

705 """

706 Integrate a Legendre series.

707

708 Returns the Legendre series coefficients `c` integrated `m` times from

709 `lbnd` along `axis`. At each iteration the resulting series is

710 **multiplied** by `scl` and an integration constant, `k`, is added.

711 The scaling factor is for use in a linear change of variable. ("Buyer

712 beware": note that, depending on what one is doing, one may want `scl`

713 to be the reciprocal of what one might expect; for more information,

714 see the Notes section below.) The argument `c` is an array of

715 coefficients from low to high degree along each axis, e.g., [1,2,3]

716 represents the series ``L_0 + 2*L_1 + 3*L_2`` while [[1,2],[1,2]]

717 represents ``1*L_0(x)*L_0(y) + 1*L_1(x)*L_0(y) + 2*L_0(x)*L_1(y) +

718 2*L_1(x)*L_1(y)`` if axis=0 is ``x`` and axis=1 is ``y``.

719

720 Parameters

721 ----------

722 c : array_like

723 Array of Legendre series coefficients. If c is multidimensional the

724 different axis correspond to different variables with the degree in

725 each axis given by the corresponding index.

726 m : int, optional

727 Order of integration, must be positive. (Default: 1)

728 k : {[], list, scalar}, optional

729 Integration constant(s). The value of the first integral at

730 ``lbnd`` is the first value in the list, the value of the second

731 integral at ``lbnd`` is the second value, etc. If ``k == []`` (the

732 default), all constants are set to zero. If ``m == 1``, a single

733 scalar can be given instead of a list.

734 lbnd : scalar, optional

735 The lower bound of the integral. (Default: 0)

736 scl : scalar, optional

737 Following each integration the result is *multiplied* by `scl`

738 before the integration constant is added. (Default: 1)

739 axis : int, optional

740 Axis over which the integral is taken. (Default: 0).

741

742 .. versionadded:: 1.7.0

743

744 Returns

745 -------

746 S : ndarray

747 Legendre series coefficient array of the integral.

748

749 Raises

750 ------

751 ValueError

752 If ``m < 0``, ``len(k) > m``, ``np.ndim(lbnd) != 0``, or

753 ``np.ndim(scl) != 0``.

754

755 See Also

756 --------

757 legder

758

759 Notes

760 -----

761 Note that the result of each integration is *multiplied* by `scl`.

762 Why is this important to note? Say one is making a linear change of

763 variable :math:`u = ax + b` in an integral relative to `x`. Then

764 :math:`dx = du/a`, so one will need to set `scl` equal to

765 :math:`1/a` - perhaps not what one would have first thought.

766

767 Also note that, in general, the result of integrating a C-series needs

768 to be "reprojected" onto the C-series basis set. Thus, typically,

769 the result of this function is "unintuitive," albeit correct; see

770 Examples section below.

771

772 Examples

773 --------

774 >>> from numpy.polynomial import legendre as L

775 >>> c = (1,2,3)

776 >>> L.legint(c)

777 array([ 0.33333333, 0.4 , 0.66666667, 0.6 ]) # may vary

778 >>> L.legint(c, 3)

779 array([ 1.66666667e-02, -1.78571429e-02, 4.76190476e-02, # may vary

780 -1.73472348e-18, 1.90476190e-02, 9.52380952e-03])

781 >>> L.legint(c, k=3)

782 array([ 3.33333333, 0.4 , 0.66666667, 0.6 ]) # may vary

783 >>> L.legint(c, lbnd=-2)

784 array([ 7.33333333, 0.4 , 0.66666667, 0.6 ]) # may vary

785 >>> L.legint(c, scl=2)

786 array([ 0.66666667, 0.8 , 1.33333333, 1.2 ]) # may vary

787

788 """

789 c = np.array(c, ndmin=1, copy=True)

790 if c.dtype.char in '?bBhHiIlLqQpP':

791 c = c.astype(np.double)

792 if not np.iterable(k):

793 k = [k]

794 cnt = pu._deprecate_as_int(m, "the order of integration")

795 iaxis = pu._deprecate_as_int(axis, "the axis")

796 if cnt < 0:

797 raise ValueError("The order of integration must be non-negative")

798 if len(k) > cnt:

799 raise ValueError("Too many integration constants")

800 if np.ndim(lbnd) != 0:

801 raise ValueError("lbnd must be a scalar.")

802 if np.ndim(scl) != 0:

803 raise ValueError("scl must be a scalar.")

804 iaxis = normalize_axis_index(iaxis, c.ndim)

805

806 if cnt == 0:

807 return c

808

809 c = np.moveaxis(c, iaxis, 0)

810 k = list(k) + [0]*(cnt - len(k))

811 for i in range(cnt):

812 n = len(c)

813 c *= scl

814 if n == 1 and np.all(c[0] == 0):

815 c[0] += k[i]

816 else:

817 tmp = np.empty((n + 1,) + c.shape[1:], dtype=c.dtype)

818 tmp[0] = c[0]*0

819 tmp[1] = c[0]

820 if n > 1:

821 tmp[2] = c[1]/3

822 for j in range(2, n):

823 t = c[j]/(2*j + 1)

824 tmp[j + 1] = t

825 tmp[j - 1] -= t

826 tmp[0] += k[i] - legval(lbnd, tmp)

827 c = tmp

828 c = np.moveaxis(c, 0, iaxis)

829 return c

830

831

832 def legval(x, c, tensor=True):

833 """

834 Evaluate a Legendre series at points x.

835

836 If `c` is of length `n + 1`, this function returns the value:

837

838 .. math:: p(x) = c_0 * L_0(x) + c_1 * L_1(x) + ... + c_n * L_n(x)

839

840 The parameter `x` is converted to an array only if it is a tuple or a

841 list, otherwise it is treated as a scalar. In either case, either `x`

842 or its elements must support multiplication and addition both with

843 themselves and with the elements of `c`.

844

845 If `c` is a 1-D array, then `p(x)` will have the same shape as `x`. If

846 `c` is multidimensional, then the shape of the result depends on the

847 value of `tensor`. If `tensor` is true the shape will be c.shape[1:] +

848 x.shape. If `tensor` is false the shape will be c.shape[1:]. Note that

849 scalars have shape (,).

850

851 Trailing zeros in the coefficients will be used in the evaluation, so

852 they should be avoided if efficiency is a concern.

853

854 Parameters

855 ----------

856 x : array_like, compatible object

857 If `x` is a list or tuple, it is converted to an ndarray, otherwise

858 it is left unchanged and treated as a scalar. In either case, `x`

859 or its elements must support addition and multiplication with

860 themselves and with the elements of `c`.

861 c : array_like

862 Array of coefficients ordered so that the coefficients for terms of

863 degree n are contained in c[n]. If `c` is multidimensional the

864 remaining indices enumerate multiple polynomials. In the two

865 dimensional case the coefficients may be thought of as stored in

866 the columns of `c`.

867 tensor : boolean, optional

868 If True, the shape of the coefficient array is extended with ones

869 on the right, one for each dimension of `x`. Scalars have dimension 0

870 for this action. The result is that every column of coefficients in

871 `c` is evaluated for every element of `x`. If False, `x` is broadcast

872 over the columns of `c` for the evaluation. This keyword is useful

873 when `c` is multidimensional. The default value is True.

874

875 .. versionadded:: 1.7.0

876

877 Returns

878 -------

879 values : ndarray, algebra_like

880 The shape of the return value is described above.

881

882 See Also

883 --------

884 legval2d, leggrid2d, legval3d, leggrid3d

885

886 Notes

887 -----

888 The evaluation uses Clenshaw recursion, aka synthetic division.

889

890 """

891 c = np.array(c, ndmin=1, copy=False)

892 if c.dtype.char in '?bBhHiIlLqQpP':

893 c = c.astype(np.double)

894 if isinstance(x, (tuple, list)):

895 x = np.asarray(x)

896 if isinstance(x, np.ndarray) and tensor:

897 c = c.reshape(c.shape + (1,)*x.ndim)

898

899 if len(c) == 1:

900 c0 = c[0]

901 c1 = 0

902 elif len(c) == 2:

903 c0 = c[0]

904 c1 = c[1]

905 else:

906 nd = len(c)

907 c0 = c[-2]

908 c1 = c[-1]

909 for i in range(3, len(c) + 1):

910 tmp = c0

911 nd = nd - 1

912 c0 = c[-i] - (c1*(nd - 1))/nd

913 c1 = tmp + (c1*x*(2*nd - 1))/nd

914 return c0 + c1*x

915

916

917 def legval2d(x, y, c):

918 """

919 Evaluate a 2-D Legendre series at points (x, y).

920

921 This function returns the values:

922

923 .. math:: p(x,y) = \\sum_{i,j} c_{i,j} * L_i(x) * L_j(y)

924

925 The parameters `x` and `y` are converted to arrays only if they are

926 tuples or a lists, otherwise they are treated as a scalars and they

927 must have the same shape after conversion. In either case, either `x`

928 and `y` or their elements must support multiplication and addition both

929 with themselves and with the elements of `c`.

930

931 If `c` is a 1-D array a one is implicitly appended to its shape to make

932 it 2-D. The shape of the result will be c.shape[2:] + x.shape.

933

934 Parameters

935 ----------

936 x, y : array_like, compatible objects

937 The two dimensional series is evaluated at the points `(x, y)`,

938 where `x` and `y` must have the same shape. If `x` or `y` is a list

939 or tuple, it is first converted to an ndarray, otherwise it is left

940 unchanged and if it isn't an ndarray it is treated as a scalar.

941 c : array_like

942 Array of coefficients ordered so that the coefficient of the term

943 of multi-degree i,j is contained in ``c[i,j]``. If `c` has

944 dimension greater than two the remaining indices enumerate multiple

945 sets of coefficients.

946

947 Returns

948 -------

949 values : ndarray, compatible object

950 The values of the two dimensional Legendre series at points formed

951 from pairs of corresponding values from `x` and `y`.

952

953 See Also

954 --------

955 legval, leggrid2d, legval3d, leggrid3d

956

957 Notes

958 -----

959

960 .. versionadded:: 1.7.0

961

962 """

963 return pu._valnd(legval, c, x, y)

964

965

966 def leggrid2d(x, y, c):

967 """

968 Evaluate a 2-D Legendre series on the Cartesian product of x and y.

969

970 This function returns the values:

971

972 .. math:: p(a,b) = \\sum_{i,j} c_{i,j} * L_i(a) * L_j(b)

973

974 where the points `(a, b)` consist of all pairs formed by taking

975 `a` from `x` and `b` from `y`. The resulting points form a grid with

976 `x` in the first dimension and `y` in the second.

977

978 The parameters `x` and `y` are converted to arrays only if they are

979 tuples or a lists, otherwise they are treated as a scalars. In either

980 case, either `x` and `y` or their elements must support multiplication

981 and addition both with themselves and with the elements of `c`.

982

983 If `c` has fewer than two dimensions, ones are implicitly appended to

984 its shape to make it 2-D. The shape of the result will be c.shape[2:] +

985 x.shape + y.shape.

986

987 Parameters

988 ----------

989 x, y : array_like, compatible objects

990 The two dimensional series is evaluated at the points in the

991 Cartesian product of `x` and `y`. If `x` or `y` is a list or

992 tuple, it is first converted to an ndarray, otherwise it is left

993 unchanged and, if it isn't an ndarray, it is treated as a scalar.

994 c : array_like

995 Array of coefficients ordered so that the coefficient of the term of

996 multi-degree i,j is contained in `c[i,j]`. If `c` has dimension

997 greater than two the remaining indices enumerate multiple sets of

998 coefficients.

999

1000 Returns

1001 -------

1002 values : ndarray, compatible object

1003 The values of the two dimensional Chebyshev series at points in the

1004 Cartesian product of `x` and `y`.

1005

1006 See Also

1007 --------

1008 legval, legval2d, legval3d, leggrid3d

1009

1010 Notes

1011 -----

1012

1013 .. versionadded:: 1.7.0

1014

1015 """

1016 return pu._gridnd(legval, c, x, y)

1017

1018

1019 def legval3d(x, y, z, c):

1020 """

1021 Evaluate a 3-D Legendre series at points (x, y, z).

1022

1023 This function returns the values:

1024

1025 .. math:: p(x,y,z) = \\sum_{i,j,k} c_{i,j,k} * L_i(x) * L_j(y) * L_k(z)

1026

1027 The parameters `x`, `y`, and `z` are converted to arrays only if

1028 they are tuples or a lists, otherwise they are treated as a scalars and

1029 they must have the same shape after conversion. In either case, either

1030 `x`, `y`, and `z` or their elements must support multiplication and

1031 addition both with themselves and with the elements of `c`.

1032

1033 If `c` has fewer than 3 dimensions, ones are implicitly appended to its

1034 shape to make it 3-D. The shape of the result will be c.shape[3:] +

1035 x.shape.

1036

1037 Parameters

1038 ----------

1039 x, y, z : array_like, compatible object

1040 The three dimensional series is evaluated at the points

1041 `(x, y, z)`, where `x`, `y`, and `z` must have the same shape. If

1042 any of `x`, `y`, or `z` is a list or tuple, it is first converted

1043 to an ndarray, otherwise it is left unchanged and if it isn't an

1044 ndarray it is treated as a scalar.

1045 c : array_like

1046 Array of coefficients ordered so that the coefficient of the term of

1047 multi-degree i,j,k is contained in ``c[i,j,k]``. If `c` has dimension

1048 greater than 3 the remaining indices enumerate multiple sets of

1049 coefficients.

1050

1051 Returns

1052 -------

1053 values : ndarray, compatible object

1054 The values of the multidimensional polynomial on points formed with

1055 triples of corresponding values from `x`, `y`, and `z`.

1056

1057 See Also

1058 --------

1059 legval, legval2d, leggrid2d, leggrid3d

1060

1061 Notes

1062 -----

1063

1064 .. versionadded:: 1.7.0

1065

1066 """

1067 return pu._valnd(legval, c, x, y, z)

1068

1069

1070 def leggrid3d(x, y, z, c):

1071 """

1072 Evaluate a 3-D Legendre series on the Cartesian product of x, y, and z.

1073

1074 This function returns the values:

1075

1076 .. math:: p(a,b,c) = \\sum_{i,j,k} c_{i,j,k} * L_i(a) * L_j(b) * L_k(c)

1077

1078 where the points `(a, b, c)` consist of all triples formed by taking

1079 `a` from `x`, `b` from `y`, and `c` from `z`. The resulting points form

1080 a grid with `x` in the first dimension, `y` in the second, and `z` in

1081 the third.

1082

1083 The parameters `x`, `y`, and `z` are converted to arrays only if they

1084 are tuples or a lists, otherwise they are treated as a scalars. In

1085 either case, either `x`, `y`, and `z` or their elements must support

1086 multiplication and addition both with themselves and with the elements

1087 of `c`.

1088

1089 If `c` has fewer than three dimensions, ones are implicitly appended to

1090 its shape to make it 3-D. The shape of the result will be c.shape[3:] +

1091 x.shape + y.shape + z.shape.

1092

1093 Parameters

1094 ----------

1095 x, y, z : array_like, compatible objects

1096 The three dimensional series is evaluated at the points in the

1097 Cartesian product of `x`, `y`, and `z`. If `x`,`y`, or `z` is a

1098 list or tuple, it is first converted to an ndarray, otherwise it is

1099 left unchanged and, if it isn't an ndarray, it is treated as a

1100 scalar.

1101 c : array_like

1102 Array of coefficients ordered so that the coefficients for terms of

1103 degree i,j are contained in ``c[i,j]``. If `c` has dimension

1104 greater than two the remaining indices enumerate multiple sets of

1105 coefficients.

1106

1107 Returns

1108 -------

1109 values : ndarray, compatible object

1110 The values of the two dimensional polynomial at points in the Cartesian

1111 product of `x` and `y`.

1112

1113 See Also

1114 --------

1115 legval, legval2d, leggrid2d, legval3d

1116

1117 Notes

1118 -----

1119

1120 .. versionadded:: 1.7.0

1121

1122 """

1123 return pu._gridnd(legval, c, x, y, z)

1124

1125

1126 def legvander(x, deg):

1127 """Pseudo-Vandermonde matrix of given degree.

1128

1129 Returns the pseudo-Vandermonde matrix of degree `deg` and sample points

1130 `x`. The pseudo-Vandermonde matrix is defined by

1131

1132 .. math:: V[..., i] = L_i(x)

1133

1134 where `0 <= i <= deg`. The leading indices of `V` index the elements of

1135 `x` and the last index is the degree of the Legendre polynomial.

1136

1137 If `c` is a 1-D array of coefficients of length `n + 1` and `V` is the

1138 array ``V = legvander(x, n)``, then ``np.dot(V, c)`` and

1139 ``legval(x, c)`` are the same up to roundoff. This equivalence is

1140 useful both for least squares fitting and for the evaluation of a large

1141 number of Legendre series of the same degree and sample points.

1142

1143 Parameters

1144 ----------

1145 x : array_like

1146 Array of points. The dtype is converted to float64 or complex128

1147 depending on whether any of the elements are complex. If `x` is

1148 scalar it is converted to a 1-D array.

1149 deg : int

1150 Degree of the resulting matrix.

1151

1152 Returns

1153 -------

1154 vander : ndarray

1155 The pseudo-Vandermonde matrix. The shape of the returned matrix is

1156 ``x.shape + (deg + 1,)``, where The last index is the degree of the

1157 corresponding Legendre polynomial. The dtype will be the same as

1158 the converted `x`.

1159

1160 """

1161 ideg = pu._deprecate_as_int(deg, "deg")

1162 if ideg < 0:

1163 raise ValueError("deg must be non-negative")

1164

1165 x = np.array(x, copy=False, ndmin=1) + 0.0

1166 dims = (ideg + 1,) + x.shape

1167 dtyp = x.dtype

1168 v = np.empty(dims, dtype=dtyp)

1169 # Use forward recursion to generate the entries. This is not as accurate

1170 # as reverse recursion in this application but it is more efficient.

1171 v[0] = x*0 + 1

1172 if ideg > 0:

1173 v[1] = x

1174 for i in range(2, ideg + 1):

1175 v[i] = (v[i-1]*x*(2*i - 1) - v[i-2]*(i - 1))/i

1176 return np.moveaxis(v, 0, -1)

1177

1178

1179 def legvander2d(x, y, deg):

1180 """Pseudo-Vandermonde matrix of given degrees.

1181

1182 Returns the pseudo-Vandermonde matrix of degrees `deg` and sample

1183 points `(x, y)`. The pseudo-Vandermonde matrix is defined by

1184

1185 .. math:: V[..., (deg[1] + 1)*i + j] = L_i(x) * L_j(y),

1186

1187 where `0 <= i <= deg[0]` and `0 <= j <= deg[1]`. The leading indices of

1188 `V` index the points `(x, y)` and the last index encodes the degrees of

1189 the Legendre polynomials.

1190

1191 If ``V = legvander2d(x, y, [xdeg, ydeg])``, then the columns of `V`

1192 correspond to the elements of a 2-D coefficient array `c` of shape

1193 (xdeg + 1, ydeg + 1) in the order

1194

1195 .. math:: c_{00}, c_{01}, c_{02} ... , c_{10}, c_{11}, c_{12} ...

1196

1197 and ``np.dot(V, c.flat)`` and ``legval2d(x, y, c)`` will be the same

1198 up to roundoff. This equivalence is useful both for least squares

1199 fitting and for the evaluation of a large number of 2-D Legendre

1200 series of the same degrees and sample points.

1201

1202 Parameters

1203 ----------

1204 x, y : array_like

1205 Arrays of point coordinates, all of the same shape. The dtypes

1206 will be converted to either float64 or complex128 depending on

1207 whether any of the elements are complex. Scalars are converted to

1208 1-D arrays.

1209 deg : list of ints

1210 List of maximum degrees of the form [x_deg, y_deg].

1211

1212 Returns

1213 -------

1214 vander2d : ndarray

1215 The shape of the returned matrix is ``x.shape + (order,)``, where

1216 :math:`order = (deg[0]+1)*(deg[1]+1)`. The dtype will be the same

1217 as the converted `x` and `y`.

1218

1219 See Also

1220 --------

1221 legvander, legvander3d, legval2d, legval3d

1222

1223 Notes

1224 -----

1225

1226 .. versionadded:: 1.7.0

1227

1228 """

1229 return pu._vander_nd_flat((legvander, legvander), (x, y), deg)

1230

1231

1232 def legvander3d(x, y, z, deg):

1233 """Pseudo-Vandermonde matrix of given degrees.

1234

1235 Returns the pseudo-Vandermonde matrix of degrees `deg` and sample

1236 points `(x, y, z)`. If `l, m, n` are the given degrees in `x, y, z`,

1237 then The pseudo-Vandermonde matrix is defined by

1238

1239 .. math:: V[..., (m+1)(n+1)i + (n+1)j + k] = L_i(x)*L_j(y)*L_k(z),

1240

1241 where `0 <= i <= l`, `0 <= j <= m`, and `0 <= j <= n`. The leading

1242 indices of `V` index the points `(x, y, z)` and the last index encodes

1243 the degrees of the Legendre polynomials.

1244

1245 If ``V = legvander3d(x, y, z, [xdeg, ydeg, zdeg])``, then the columns

1246 of `V` correspond to the elements of a 3-D coefficient array `c` of

1247 shape (xdeg + 1, ydeg + 1, zdeg + 1) in the order

1248

1249 .. math:: c_{000}, c_{001}, c_{002},... , c_{010}, c_{011}, c_{012},...

1250

1251 and ``np.dot(V, c.flat)`` and ``legval3d(x, y, z, c)`` will be the

1252 same up to roundoff. This equivalence is useful both for least squares

1253 fitting and for the evaluation of a large number of 3-D Legendre

1254 series of the same degrees and sample points.

1255

1256 Parameters

1257 ----------

1258 x, y, z : array_like

1259 Arrays of point coordinates, all of the same shape. The dtypes will

1260 be converted to either float64 or complex128 depending on whether

1261 any of the elements are complex. Scalars are converted to 1-D

1262 arrays.

1263 deg : list of ints

1264 List of maximum degrees of the form [x_deg, y_deg, z_deg].

1265

1266 Returns

1267 -------

1268 vander3d : ndarray

1269 The shape of the returned matrix is ``x.shape + (order,)``, where

1270 :math:`order = (deg[0]+1)*(deg[1]+1)*(deg[2]+1)`. The dtype will

1271 be the same as the converted `x`, `y`, and `z`.

1272

1273 See Also

1274 --------

1275 legvander, legvander3d, legval2d, legval3d

1276

1277 Notes

1278 -----

1279

1280 .. versionadded:: 1.7.0

1281

1282 """

1283 return pu._vander_nd_flat((legvander, legvander, legvander), (x, y, z), deg)

1284

1285

1286 def legfit(x, y, deg, rcond=None, full=False, w=None):

1287 """

1288 Least squares fit of Legendre series to data.

1289

1290 Return the coefficients of a Legendre series of degree `deg` that is the

1291 least squares fit to the data values `y` given at points `x`. If `y` is

1292 1-D the returned coefficients will also be 1-D. If `y` is 2-D multiple

1293 fits are done, one for each column of `y`, and the resulting

1294 coefficients are stored in the corresponding columns of a 2-D return.

1295 The fitted polynomial(s) are in the form

1296

1297 .. math:: p(x) = c_0 + c_1 * L_1(x) + ... + c_n * L_n(x),

1298

1299 where `n` is `deg`.

1300

1301 Parameters

1302 ----------

1303 x : array_like, shape (M,)

1304 x-coordinates of the M sample points ``(x[i], y[i])``.

1305 y : array_like, shape (M,) or (M, K)

1306 y-coordinates of the sample points. Several data sets of sample

1307 points sharing the same x-coordinates can be fitted at once by

1308 passing in a 2D-array that contains one dataset per column.

1309 deg : int or 1-D array_like

1310 Degree(s) of the fitting polynomials. If `deg` is a single integer

1311 all terms up to and including the `deg`'th term are included in the

1312 fit. For NumPy versions >= 1.11.0 a list of integers specifying the

1313 degrees of the terms to include may be used instead.

1314 rcond : float, optional

1315 Relative condition number of the fit. Singular values smaller than

1316 this relative to the largest singular value will be ignored. The

1317 default value is len(x)*eps, where eps is the relative precision of

1318 the float type, about 2e-16 in most cases.

1319 full : bool, optional

1320 Switch determining nature of return value. When it is False (the

1321 default) just the coefficients are returned, when True diagnostic

1322 information from the singular value decomposition is also returned.

1323 w : array_like, shape (`M`,), optional

1324 Weights. If not None, the weight ``w[i]`` applies to the unsquared

1325 residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are

1326 chosen so that the errors of the products ``w[i]*y[i]`` all have the

1327 same variance. When using inverse-variance weighting, use

1328 ``w[i] = 1/sigma(y[i])``. The default value is None.

1329

1330 .. versionadded:: 1.5.0

1331

1332 Returns

1333 -------

1334 coef : ndarray, shape (M,) or (M, K)

1335 Legendre coefficients ordered from low to high. If `y` was

1336 2-D, the coefficients for the data in column k of `y` are in

1337 column `k`. If `deg` is specified as a list, coefficients for

1338 terms not included in the fit are set equal to zero in the

1339 returned `coef`.

1340

1341 [residuals, rank, singular_values, rcond] : list

1342 These values are only returned if ``full == True``

1343

1344 - residuals -- sum of squared residuals of the least squares fit

1345 - rank -- the numerical rank of the scaled Vandermonde matrix

1346 - singular_values -- singular values of the scaled Vandermonde matrix

1347 - rcond -- value of `rcond`.

1348

1349 For more details, see `numpy.linalg.lstsq`.

1350

1351 Warns

1352 -----

1353 RankWarning

1354 The rank of the coefficient matrix in the least-squares fit is

1355 deficient. The warning is only raised if ``full == False``. The

1356 warnings can be turned off by

1357

1358 >>> import warnings

1359 >>> warnings.simplefilter('ignore', np.RankWarning)

1360

1361 See Also

1362 --------

1363 numpy.polynomial.polynomial.polyfit

1364 numpy.polynomial.chebyshev.chebfit

1365 numpy.polynomial.laguerre.lagfit

1366 numpy.polynomial.hermite.hermfit

1367 numpy.polynomial.hermite_e.hermefit

1368 legval : Evaluates a Legendre series.

1369 legvander : Vandermonde matrix of Legendre series.

1370 legweight : Legendre weight function (= 1).

1371 numpy.linalg.lstsq : Computes a least-squares fit from the matrix.

1372 scipy.interpolate.UnivariateSpline : Computes spline fits.

1373

1374 Notes

1375 -----

1376 The solution is the coefficients of the Legendre series `p` that

1377 minimizes the sum of the weighted squared errors

1378

1379 .. math:: E = \\sum_j w_j^2 * |y_j - p(x_j)|^2,

1380

1381 where :math:`w_j` are the weights. This problem is solved by setting up

1382 as the (typically) overdetermined matrix equation

1383

1384 .. math:: V(x) * c = w * y,

1385

1386 where `V` is the weighted pseudo Vandermonde matrix of `x`, `c` are the

1387 coefficients to be solved for, `w` are the weights, and `y` are the

1388 observed values. This equation is then solved using the singular value

1389 decomposition of `V`.

1390

1391 If some of the singular values of `V` are so small that they are

1392 neglected, then a `RankWarning` will be issued. This means that the

1393 coefficient values may be poorly determined. Using a lower order fit

1394 will usually get rid of the warning. The `rcond` parameter can also be

1395 set to a value smaller than its default, but the resulting fit may be

1396 spurious and have large contributions from roundoff error.

1397

1398 Fits using Legendre series are usually better conditioned than fits

1399 using power series, but much can depend on the distribution of the

1400 sample points and the smoothness of the data. If the quality of the fit

1401 is inadequate splines may be a good alternative.

1402

1403 References

1404 ----------

1405 .. [1] Wikipedia, "Curve fitting",

1406 https://en.wikipedia.org/wiki/Curve_fitting

1407

1408 Examples

1409 --------

1410

1411 """

1412 return pu._fit(legvander, x, y, deg, rcond, full, w)

1413

1414

1415 def legcompanion(c):

1416 """Return the scaled companion matrix of c.

1417

1418 The basis polynomials are scaled so that the companion matrix is

1419 symmetric when `c` is an Legendre basis polynomial. This provides

1420 better eigenvalue estimates than the unscaled case and for basis

1421 polynomials the eigenvalues are guaranteed to be real if

1422 `numpy.linalg.eigvalsh` is used to obtain them.

1423

1424 Parameters

1425 ----------

1426 c : array_like

1427 1-D array of Legendre series coefficients ordered from low to high

1428 degree.

1429

1430 Returns

1431 -------

1432 mat : ndarray

1433 Scaled companion matrix of dimensions (deg, deg).

1434

1435 Notes

1436 -----

1437

1438 .. versionadded:: 1.7.0

1439

1440 """

1441 # c is a trimmed copy

1442 [c] = pu.as_series([c])

1443 if len(c) < 2:

1444 raise ValueError('Series must have maximum degree of at least 1.')

1445 if len(c) == 2:

1446 return np.array([[-c[0]/c[1]]])

1447

1448 n = len(c) - 1

1449 mat = np.zeros((n, n), dtype=c.dtype)

1450 scl = 1./np.sqrt(2*np.arange(n) + 1)

1451 top = mat.reshape(-1)[1::n+1]

1452 bot = mat.reshape(-1)[n::n+1]

1453 top[...] = np.arange(1, n)*scl[:n-1]*scl[1:n]

1454 bot[...] = top

1455 mat[:, -1] -= (c[:-1]/c[-1])*(scl/scl[-1])*(n/(2*n - 1))

1456 return mat

1457

1458

1459 def legroots(c):

1460 """

1461 Compute the roots of a Legendre series.

1462

1463 Return the roots (a.k.a. "zeros") of the polynomial

1464

1465 .. math:: p(x) = \\sum_i c[i] * L_i(x).

1466

1467 Parameters

1468 ----------

1469 c : 1-D array_like

1470 1-D array of coefficients.

1471

1472 Returns

1473 -------

1474 out : ndarray

1475 Array of the roots of the series. If all the roots are real,

1476 then `out` is also real, otherwise it is complex.

1477

1478 See Also

1479 --------

1480 numpy.polynomial.polynomial.polyroots

1481 numpy.polynomial.chebyshev.chebroots

1482 numpy.polynomial.laguerre.lagroots

1483 numpy.polynomial.hermite.hermroots

1484 numpy.polynomial.hermite_e.hermeroots

1485

1486 Notes

1487 -----

1488 The root estimates are obtained as the eigenvalues of the companion

1489 matrix, Roots far from the origin of the complex plane may have large

1490 errors due to the numerical instability of the series for such values.

1491 Roots with multiplicity greater than 1 will also show larger errors as

1492 the value of the series near such points is relatively insensitive to

1493 errors in the roots. Isolated roots near the origin can be improved by

1494 a few iterations of Newton's method.

1495

1496 The Legendre series basis polynomials aren't powers of ``x`` so the

1497 results of this function may seem unintuitive.

1498

1499 Examples

1500 --------

1501 >>> import numpy.polynomial.legendre as leg

1502 >>> leg.legroots((1, 2, 3, 4)) # 4L_3 + 3L_2 + 2L_1 + 1L_0, all real roots

1503 array([-0.85099543, -0.11407192, 0.51506735]) # may vary

1504

1505 """

1506 # c is a trimmed copy

1507 [c] = pu.as_series([c])

1508 if len(c) < 2:

1509 return np.array([], dtype=c.dtype)

1510 if len(c) == 2:

1511 return np.array([-c[0]/c[1]])

1512

1513 # rotated companion matrix reduces error

1514 m = legcompanion(c)[::-1,::-1]

1515 r = la.eigvals(m)

1516 r.sort()

1517 return r

1518

1519

1520 def leggauss(deg):

1521 """

1522 Gauss-Legendre quadrature.

1523

1524 Computes the sample points and weights for Gauss-Legendre quadrature.

1525 These sample points and weights will correctly integrate polynomials of

1526 degree :math:`2*deg - 1` or less over the interval :math:`[-1, 1]` with

1527 the weight function :math:`f(x) = 1`.

1528

1529 Parameters

1530 ----------

1531 deg : int

1532 Number of sample points and weights. It must be >= 1.

1533

1534 Returns

1535 -------

1536 x : ndarray

1537 1-D ndarray containing the sample points.

1538 y : ndarray

1539 1-D ndarray containing the weights.

1540

1541 Notes

1542 -----

1543

1544 .. versionadded:: 1.7.0

1545

1546 The results have only been tested up to degree 100, higher degrees may

1547 be problematic. The weights are determined by using the fact that

1548

1549 .. math:: w_k = c / (L'_n(x_k) * L_{n-1}(x_k))

1550

1551 where :math:`c` is a constant independent of :math:`k` and :math:`x_k`

1552 is the k'th root of :math:`L_n`, and then scaling the results to get

1553 the right value when integrating 1.

1554

1555 """

1556 ideg = pu._deprecate_as_int(deg, "deg")

1557 if ideg <= 0:

1558 raise ValueError("deg must be a positive integer")

1559

1560 # first approximation of roots. We use the fact that the companion

1561 # matrix is symmetric in this case in order to obtain better zeros.

1562 c = np.array([0]*deg + [1])

1563 m = legcompanion(c)

1564 x = la.eigvalsh(m)

1565

1566 # improve roots by one application of Newton

1567 dy = legval(x, c)

1568 df = legval(x, legder(c))

1569 x -= dy/df

1570

1571 # compute the weights. We scale the factor to avoid possible numerical

1572 # overflow.

1573 fm = legval(x, c[1:])

1574 fm /= np.abs(fm).max()

1575 df /= np.abs(df).max()

1576 w = 1/(fm * df)

1577

1578 # for Legendre we can also symmetrize

1579 w = (w + w[::-1])/2

1580 x = (x - x[::-1])/2

1581

1582 # scale w to get the right value

1583 w *= 2. / w.sum()

1584

1585 return x, w

1586

1587

1588 def legweight(x):

1589 """

1590 Weight function of the Legendre polynomials.

1591

1592 The weight function is :math:`1` and the interval of integration is

1593 :math:`[-1, 1]`. The Legendre polynomials are orthogonal, but not

1594 normalized, with respect to this weight function.

1595

1596 Parameters

1597 ----------

1598 x : array_like

1599 Values at which the weight function will be computed.

1600

1601 Returns

1602 -------

1603 w : ndarray

1604 The weight function at `x`.

1605

1606 Notes

1607 -----

1608

1609 .. versionadded:: 1.7.0

1610

1611 """

1612 w = x*0.0 + 1.0

1613 return w

1614

1615#

1616# Legendre series class

1617#

1618

1619 class Legendre(ABCPolyBase):

1620 """A Legendre series class.

1621

1622 The Legendre class provides the standard Python numerical methods

1623 '+', '-', '*', '//', '%', 'divmod', '**', and '()' as well as the

1624 attributes and methods listed in the `ABCPolyBase` documentation.

1625

1626 Parameters

1627 ----------

1628 coef : array_like

1629 Legendre coefficients in order of increasing degree, i.e.,

1630 ``(1, 2, 3)`` gives ``1*P_0(x) + 2*P_1(x) + 3*P_2(x)``.

1631 domain : (2,) array_like, optional

1632 Domain to use. The interval ``[domain[0], domain[1]]`` is mapped

1633 to the interval ``[window[0], window[1]]`` by shifting and scaling.

1634 The default value is [-1, 1].

1635 window : (2,) array_like, optional

1636 Window, see `domain` for its use. The default value is [-1, 1].

1637

1638 .. versionadded:: 1.6.0

1639

1640 """

1641 # Virtual Functions

1642 _add = staticmethod(legadd)

1643 _sub = staticmethod(legsub)

1644 _mul = staticmethod(legmul)

1645 _div = staticmethod(legdiv)

1646 _pow = staticmethod(legpow)

1647 _val = staticmethod(legval)

1648 _int = staticmethod(legint)

1649 _der = staticmethod(legder)

1650 _fit = staticmethod(legfit)

1651 _line = staticmethod(legline)

1652 _roots = staticmethod(legroots)

1653 _fromroots = staticmethod(legfromroots)

1654

1655 # Virtual properties

1656 domain = np.array(legdomain)

1657 window = np.array(legdomain)

1658 basis_name = 'P'