@@ -718,6 +718,8 @@ def quad(ctx, f, *points, **kwargs):
718718 >>> quad(f, [-100, 0, 100]) # Also good
719719 3.12159332021646
720720
721+ For such integr
722+
721723 **References**
722724
723725 1. http://mathworld.wolfram.com/DoubleIntegral.html
@@ -1006,6 +1008,108 @@ def term(k):
10061008 s += ctx .nsum (term , [n , ctx .inf ])
10071009 return s
10081010
1011+ def quadsubdiv (ctx , f , interval , tol = None , maxintervals = None , ** kwargs ):
1012+ """
1013+ Computes the integral of *f* over the interval or path specified
1014+ by *interval*, using :func:`~mpmath.quad` together with adaptive
1015+ subdivision of the interval.
1016+
1017+ This function gives an accurate answer for some integrals where
1018+ :func:`~mpmath.quad` fails::
1019+
1020+ >>> mp.dps = 15; mp.pretty = True
1021+ >>> quad(lambda x: abs(sin(x)), [0, 2*pi])
1022+ 3.99900894176779
1023+ >>> quadsubdiv(lambda x: abs(sin(x)), [0, 2*pi])
1024+ 4.0
1025+ >>> quadsubdiv(sin, [0, 1000])
1026+ 0.437620923709297
1027+ >>> quadsubdiv(lambda x: 1/(1+x**2), [-100, 100])
1028+ 3.12159332021646
1029+ >>> quadsubdiv(lambda x: ceil(x), [0, 100])
1030+ 5050.0
1031+ >>> quadsubdiv(lambda x: sin(x+exp(x)), [0,8])
1032+ 0.347400172657248
1033+
1034+ The argument *maxintervals* can be set to limit the permissible
1035+ subdivision::
1036+
1037+ >>> quadsubdiv(lambda x: sin(x**2), [0,100], maxintervals=5, error=True)
1038+ (-5.40487904307774, 5.011)
1039+ >>> quadsubdiv(lambda x: sin(x**2), [0,100], maxintervals=100, error=True)
1040+ (0.631417921866934, 1.10101120134116e-17)
1041+
1042+ Subdivision does not guarantee a correct answer since, the error
1043+ estimate on subintervals may be inaccurate::
1044+
1045+ >>> quadsubdiv(lambda x: sech(10*x-2)**2 + sech(100*x-40)**4 + sech(1000*x-600)**6, [0,1], error=True)
1046+ (0.209736068833883, 1.00011000000001e-18)
1047+ >>> mp.dps = 20
1048+ >>> quadsubdiv(lambda x: sech(10*x-2)**2 + sech(100*x-40)**4 + sech(1000*x-600)**6, [0,1], error=True)
1049+ (0.21080273550054927738, 2.200000001e-24)
1050+
1051+ The second answer is correct. We can get an accurate result at lower
1052+ precision by forcing a finer initial subdivision::
1053+
1054+ >>> quadsubdiv(lambda x: sech(10*x-2)**2 + sech(100*x-40)**4 + sech(1000*x-600)**6, linspace(0,1,5))
1055+ 0.210802735500549
1056+
1057+ The following integral is too oscillatory for convergence, but we can get a
1058+ reasonable estimate::
1059+
1060+ >>> v, err = fp.quadsubdiv(lambda x: fp.sin(1/x), [0,1], error=True)
1061+ >>> round(v, 6), round(err, 6)
1062+ (0.504067, 1e-06)
1063+ >>> sin(1) - ci(1)
1064+ 0.504067061906928
1065+
1066+ """
1067+ queue = []
1068+ for i in range (len (interval )- 1 ):
1069+ queue .append ((interval [i ], interval [i + 1 ]))
1070+ total = ctx .zero
1071+ total_error = ctx .zero
1072+ if maxintervals is None :
1073+ maxintervals = 10 * ctx .prec
1074+ count = 0
1075+ quad_args = kwargs .copy ()
1076+ quad_args ["verbose" ] = False
1077+ quad_args ["error" ] = True
1078+ if tol is None :
1079+ tol = + ctx .eps
1080+ orig = ctx .prec
1081+ try :
1082+ ctx .prec += 5
1083+ while queue :
1084+ a , b = queue .pop ()
1085+ s , err = ctx .quad (f , [a , b ], ** quad_args )
1086+ if kwargs .get ("verbose" ):
1087+ print ("subinterval" , count , a , b , err )
1088+ if err < tol or count > maxintervals :
1089+ total += s
1090+ total_error += err
1091+ else :
1092+ count += 1
1093+ if count == maxintervals and kwargs .get ("verbose" ):
1094+ print ("warning: number of intervals exceeded maxintervals" )
1095+ if a == - ctx .inf and b == ctx .inf :
1096+ m = 0
1097+ elif a == - ctx .inf :
1098+ m = min (b - 1 , 2 * b )
1099+ elif b == ctx .inf :
1100+ m = max (a + 1 , 2 * a )
1101+ else :
1102+ m = a + (b - a ) / 2
1103+ queue .append ((a , m ))
1104+ queue .append ((m , b ))
1105+ finally :
1106+ ctx .prec = orig
1107+ if kwargs .get ("error" ):
1108+ return + total , + total_error
1109+ else :
1110+ return + total
1111+
10091112if __name__ == '__main__' :
10101113 import doctest
10111114 doctest .testmod ()
1115+
0 commit comments