2
2
# Copyright 2017 Christoph Groth
3
3
4
4
from collections import defaultdict
5
- from fractions import Fraction as Frac
5
+ from fractions import Fraction
6
+ from typing import Callable , List , Tuple , Union
6
7
7
8
import numpy as np
8
9
from numpy .testing import assert_allclose
11
12
eps = np .spacing (1 )
12
13
13
14
14
- def legendre (n ) :
15
+ def legendre (n : int ) -> List [ List [ Fraction ]] :
15
16
"""Return the first n Legendre polynomials.
16
17
17
18
The polynomials have *standard* normalization, i.e.
18
19
int_{-1}^1 dx L_n(x) L_m(x) = delta(m, n) * 2 / (2 * n + 1).
19
20
20
21
The return value is a list of list of fraction.Fraction instances.
21
22
"""
22
- result = [[Frac (1 )], [Frac (0 ), Frac (1 )]]
23
+ result = [[Fraction (1 )], [Fraction (0 ), Fraction (1 )]]
23
24
if n <= 2 :
24
25
return result [:n ]
25
26
for i in range (2 , n ):
26
27
# Use Bonnet's recursion formula.
27
- new = (i + 1 ) * [Frac (0 )]
28
+ new = (i + 1 ) * [Fraction (0 )]
28
29
new [1 :] = (r * (2 * i - 1 ) for r in result [- 1 ])
29
30
new [:- 2 ] = (n - r * (i - 1 ) for n , r in zip (new [:- 2 ], result [- 2 ]))
30
31
new [:] = (n / i for n in new )
31
32
result .append (new )
32
33
return result
33
34
34
35
35
- def newton (n ) :
36
+ def newton (n : int ) -> np . ndarray :
36
37
"""Compute the monomial coefficients of the Newton polynomial over the
37
38
nodes of the n-point Clenshaw-Curtis quadrature rule.
38
39
"""
@@ -89,7 +90,7 @@ def newton(n):
89
90
return cf
90
91
91
92
92
- def scalar_product (a , b ) :
93
+ def scalar_product (a : List [ Fraction ] , b : List [ Fraction ]) -> Fraction :
93
94
"""Compute the polynomial scalar product int_-1^1 dx a(x) b(x).
94
95
95
96
The args must be sequences of polynomial coefficients. This
@@ -110,7 +111,7 @@ def scalar_product(a, b):
110
111
return 2 * sum (c [i ] / (i + 1 ) for i in range (0 , lc , 2 ))
111
112
112
113
113
- def calc_bdef (ns ) :
114
+ def calc_bdef (ns : Tuple [ int , int , int , int ]) -> List [ np . ndarray ] :
114
115
"""Calculate the decompositions of Newton polynomials (over the nodes
115
116
of the n-point Clenshaw-Curtis quadrature rule) in terms of
116
117
Legandre polynomials.
@@ -123,7 +124,7 @@ def calc_bdef(ns):
123
124
result = []
124
125
for n in ns :
125
126
poly = []
126
- a = list (map (Frac , newton (n )))
127
+ a = list (map (Fraction , newton (n )))
127
128
for b in legs [: n + 1 ]:
128
129
igral = scalar_product (a , b )
129
130
@@ -145,7 +146,7 @@ def calc_bdef(ns):
145
146
b_def = calc_bdef (n )
146
147
147
148
148
- def calc_V (xi , n ) :
149
+ def calc_V (xi : np . ndarray , n : int ) -> np . ndarray :
149
150
V = [np .ones (xi .shape ), xi .copy ()]
150
151
for i in range (2 , n ):
151
152
V .append ((2 * i - 1 ) / i * xi * V [- 1 ] - (i - 1 ) / i * V [- 2 ])
@@ -183,7 +184,7 @@ def calc_V(xi, n):
183
184
gamma = np .concatenate ([[0 , 0 ], np .sqrt (k [2 :] ** 2 / (4 * k [2 :] ** 2 - 1 ))])
184
185
185
186
186
- def _downdate (c , nans , depth ) :
187
+ def _downdate (c : np . ndarray , nans : List [ int ] , depth : int ) -> None :
187
188
# This is algorithm 5 from the thesis of Pedro Gonnet.
188
189
b = b_def [depth ].copy ()
189
190
m = n [depth ] - 1
@@ -200,7 +201,7 @@ def _downdate(c, nans, depth):
200
201
m -= 1
201
202
202
203
203
- def _zero_nans (fx ) :
204
+ def _zero_nans (fx : np . ndarray ) -> List [ int ] :
204
205
nans = []
205
206
for i in range (len (fx )):
206
207
if not np .isfinite (fx [i ]):
@@ -209,7 +210,7 @@ def _zero_nans(fx):
209
210
return nans
210
211
211
212
212
- def _calc_coeffs (fx , depth ) :
213
+ def _calc_coeffs (fx : np . ndarray , depth : int ) -> np . ndarray :
213
214
"""Caution: this function modifies fx."""
214
215
nans = _zero_nans (fx )
215
216
c_new = V_inv [depth ] @ fx
@@ -220,7 +221,7 @@ def _calc_coeffs(fx, depth):
220
221
221
222
222
223
class DivergentIntegralError (ValueError ):
223
- def __init__ (self , msg , igral , err , nr_points ) :
224
+ def __init__ (self , msg : str , igral : float , err : None , nr_points : int ) -> None :
224
225
self .igral = igral
225
226
self .err = err
226
227
self .nr_points = nr_points
@@ -230,19 +231,23 @@ def __init__(self, msg, igral, err, nr_points):
230
231
class _Interval :
231
232
__slots__ = ["a" , "b" , "c" , "fx" , "igral" , "err" , "depth" , "rdepth" , "ndiv" , "c00" ]
232
233
233
- def __init__ (self , a , b , depth , rdepth ):
234
+ def __init__ (
235
+ self , a : Union [int , float ], b : Union [int , float ], depth : int , rdepth : int
236
+ ) -> None :
234
237
self .a = a
235
238
self .b = b
236
239
self .depth = depth
237
240
self .rdepth = rdepth
238
241
239
- def points (self ):
242
+ def points (self ) -> np . ndarray :
240
243
a = self .a
241
244
b = self .b
242
245
return (a + b ) / 2 + (b - a ) * xi [self .depth ] / 2
243
246
244
247
@classmethod
245
- def make_first (cls , f , a , b , depth = 2 ):
248
+ def make_first (
249
+ cls , f : Callable , a : int , b : int , depth : int = 2
250
+ ) -> Tuple ["_Interval" , int ]:
246
251
ival = _Interval (a , b , depth , 1 )
247
252
fx = f (ival .points ())
248
253
ival .c = _calc_coeffs (fx , depth )
@@ -251,7 +256,7 @@ def make_first(cls, f, a, b, depth=2):
251
256
ival .ndiv = 0
252
257
return ival , n [depth ]
253
258
254
- def calc_igral_and_err (self , c_old ) :
259
+ def calc_igral_and_err (self , c_old : np . ndarray ) -> float :
255
260
self .c = c_new = _calc_coeffs (self .fx , self .depth )
256
261
c_diff = np .zeros (max (len (c_old ), len (c_new )))
257
262
c_diff [: len (c_old )] = c_old
@@ -262,7 +267,9 @@ def calc_igral_and_err(self, c_old):
262
267
self .err = w * c_diff
263
268
return c_diff
264
269
265
- def split (self , f ):
270
+ def split (
271
+ self , f : Callable
272
+ ) -> Union [Tuple [Tuple [float , float , float ], int ], Tuple [List ["_Interval" ], int ]]:
266
273
m = (self .a + self .b ) / 2
267
274
f_center = self .fx [(len (self .fx ) - 1 ) // 2 ]
268
275
@@ -287,7 +294,7 @@ def split(self, f):
287
294
288
295
return ivals , nr_points
289
296
290
- def refine (self , f ) :
297
+ def refine (self , f : Callable ) -> Tuple [ np . ndarray , bool , int ] :
291
298
"""Increase degree of interval."""
292
299
self .depth = depth = self .depth + 1
293
300
points = self .points ()
@@ -299,7 +306,9 @@ def refine(self, f):
299
306
return points , split , n [depth ] - n [depth - 1 ]
300
307
301
308
302
- def algorithm_4 (f , a , b , tol , N_loops = int (1e9 )):
309
+ def algorithm_4 (
310
+ f : Callable , a : int , b : int , tol : float , N_loops : int = int (1e9 )
311
+ ) -> Tuple [float , float , int , List ["_Interval" ]]:
303
312
"""ALGORITHM_4 evaluates an integral using adaptive quadrature. The
304
313
algorithm uses Clenshaw-Curtis quadrature rules of increasing
305
314
degree in each interval and bisects the interval if either the
@@ -403,37 +412,39 @@ def algorithm_4(f, a, b, tol, N_loops=int(1e9)):
403
412
return igral , err , nr_points , ivals
404
413
405
414
406
- ################ Tests ################
415
+ # ############### Tests ################
407
416
408
417
409
- def f0 (x ) :
418
+ def f0 (x : Union [ float , np . ndarray ]) -> Union [ float , np . ndarray ] :
410
419
return x * np .sin (1 / x ) * np .sqrt (abs (1 - x ))
411
420
412
421
413
422
def f7 (x ):
414
423
return x ** - 0.5
415
424
416
425
417
- def f24 (x ) :
426
+ def f24 (x : Union [ float , np . ndarray ]) -> Union [ float , np . ndarray ] :
418
427
return np .floor (np .exp (x ))
419
428
420
429
421
- def f21 (x ) :
430
+ def f21 (x : Union [ float , np . ndarray ]) -> Union [ float , np . ndarray ] :
422
431
y = 0
423
432
for i in range (1 , 4 ):
424
433
y += 1 / np .cosh (20 ** i * (x - 2 * i / 10 ))
425
434
return y
426
435
427
436
428
- def f63 (x , alpha , beta ):
437
+ def f63 (
438
+ x : Union [float , np .ndarray ], alpha : float , beta : float
439
+ ) -> Union [float , np .ndarray ]:
429
440
return abs (x - beta ) ** alpha
430
441
431
442
432
443
def F63 (x , alpha , beta ):
433
444
return (x - beta ) * abs (x - beta ) ** alpha / (alpha + 1 )
434
445
435
446
436
- def fdiv (x ) :
447
+ def fdiv (x : Union [ float , np . ndarray ]) -> Union [ float , np . ndarray ] :
437
448
return abs (x - 0.987654321 ) ** - 1.1
438
449
439
450
@@ -461,7 +472,9 @@ def test_scalar_product(n=33):
461
472
selection = [0 , 5 , 7 , n - 1 ]
462
473
for i in selection :
463
474
for j in selection :
464
- assert scalar_product (legs [i ], legs [j ]) == ((i == j ) and Frac (2 , 2 * i + 1 ))
475
+ assert scalar_product (legs [i ], legs [j ]) == (
476
+ (i == j ) and Fraction (2 , 2 * i + 1 )
477
+ )
465
478
466
479
467
480
def simple_newton (n ):
0 commit comments