commit
e5a325d024
@ -0,0 +1,577 @@
|
|||||||
|
'''
|
||||||
|
Created on 20. aug. 2015
|
||||||
|
|
||||||
|
@author: pab
|
||||||
|
'''
|
||||||
|
from __future__ import division
|
||||||
|
import numpy as np
|
||||||
|
import warnings
|
||||||
|
import numdifftools as nd # @UnresolvedImport
|
||||||
|
import numdifftools.nd_algopy as nda # @UnresolvedImport
|
||||||
|
from numdifftools.limits import Limit # @UnresolvedImport
|
||||||
|
from numpy import linalg
|
||||||
|
from numpy.polynomial.chebyshev import chebval, Chebyshev
|
||||||
|
from numpy.polynomial import polynomial
|
||||||
|
from wafo.misc import piecewise, findcross, ecross
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
EPS = np.finfo(float).eps
|
||||||
|
_EPS = EPS
|
||||||
|
finfo = np.finfo(float)
|
||||||
|
_TINY = finfo.tiny
|
||||||
|
_HUGE = finfo.max
|
||||||
|
dea3 = nd.dea3
|
||||||
|
|
||||||
|
|
||||||
|
class PolyBasis(object):
|
||||||
|
|
||||||
|
def derivative(self, t, k, n=1):
|
||||||
|
c = np.zeros(k + 1)
|
||||||
|
c[k] = 1
|
||||||
|
dc = polynomial.polyder(c, m=n)
|
||||||
|
return polynomial.polyval(t, dc)
|
||||||
|
|
||||||
|
def eval(self, t, c):
|
||||||
|
return polynomial.polyval(t, c)
|
||||||
|
|
||||||
|
def __call__(self, t, k):
|
||||||
|
return t**k
|
||||||
|
poly_basis = PolyBasis()
|
||||||
|
|
||||||
|
|
||||||
|
class ChebyshevBasis(object):
|
||||||
|
|
||||||
|
def derivative(self, t, k, n=1):
|
||||||
|
c = np.zeros(k + 1)
|
||||||
|
c[k] = 1
|
||||||
|
cheb = Chebyshev(c)
|
||||||
|
dcheb = cheb.deriv(m=n)
|
||||||
|
return chebval(t, dcheb.coef)
|
||||||
|
|
||||||
|
def eval(self, t, c):
|
||||||
|
return chebval(t, c)
|
||||||
|
|
||||||
|
def __call__(self, t, k):
|
||||||
|
c = np.zeros(k + 1)
|
||||||
|
c[k] = 1
|
||||||
|
return chebval(t, c)
|
||||||
|
chebyshev_basis = ChebyshevBasis()
|
||||||
|
|
||||||
|
|
||||||
|
def richardson(Q, k):
|
||||||
|
# license BSD
|
||||||
|
# Richardson extrapolation with parameter estimation
|
||||||
|
c = np.real((Q[k - 1] - Q[k - 2]) / (Q[k] - Q[k - 1])) - 1.
|
||||||
|
# The lower bound 0.07 admits the singularity x.^-0.9
|
||||||
|
c = max(c, 0.07)
|
||||||
|
R = Q[k] + (Q[k] - Q[k - 1]) / c
|
||||||
|
return R
|
||||||
|
|
||||||
|
|
||||||
|
def evans_webster_weights(omega, gg, dgg, x, basis, *args, **kwds):
|
||||||
|
|
||||||
|
def Psi(t, k):
|
||||||
|
return dgg(t, *args, **kwds) * basis(t, k)
|
||||||
|
|
||||||
|
j_w = 1j * omega
|
||||||
|
nn = len(x)
|
||||||
|
A = np.zeros((nn, nn), dtype=complex)
|
||||||
|
F = np.zeros((nn,), dtype=complex)
|
||||||
|
|
||||||
|
dbasis = basis.derivative
|
||||||
|
lim_gg = Limit(gg)
|
||||||
|
b1 = np.exp(j_w*lim_gg(1, *args, **kwds))
|
||||||
|
if np.isnan(b1):
|
||||||
|
b1 = 0.0
|
||||||
|
a1 = np.exp(j_w*lim_gg(-1, *args, **kwds))
|
||||||
|
if np.isnan(a1):
|
||||||
|
a1 = 0.0
|
||||||
|
|
||||||
|
lim_Psi = Limit(Psi)
|
||||||
|
for k in range(nn):
|
||||||
|
F[k] = basis(1, k)*b1 - basis(-1, k)*a1
|
||||||
|
A[k] = (dbasis(x, k, n=1) + j_w * lim_Psi(x, k))
|
||||||
|
|
||||||
|
LS = linalg.lstsq(A, F)
|
||||||
|
return LS[0]
|
||||||
|
|
||||||
|
|
||||||
|
def osc_weights(omega, g, dg, x, basis, ab, *args, **kwds):
|
||||||
|
w = []
|
||||||
|
|
||||||
|
for a, b in zip(ab[::2], ab[1::2]):
|
||||||
|
scale = (b - a) / 2
|
||||||
|
offset = (a + b) / 2
|
||||||
|
|
||||||
|
def gg(t):
|
||||||
|
return g(scale * t + offset, *args, **kwds)
|
||||||
|
|
||||||
|
def dgg(t):
|
||||||
|
return scale * dg(scale * t + offset, *args, **kwds)
|
||||||
|
|
||||||
|
w.append(evans_webster_weights(omega, gg, dgg, x, basis))
|
||||||
|
|
||||||
|
return np.asarray(w).ravel()
|
||||||
|
|
||||||
|
|
||||||
|
class QuadOsc(object):
|
||||||
|
info = namedtuple('info', ['error_estimate', 'n'])
|
||||||
|
|
||||||
|
def __init__(self, f, g, dg=None, a=-1, b=1, basis=chebyshev_basis, s=15,
|
||||||
|
precision=10, endpoints=False, maxiter=17, full_output=False):
|
||||||
|
self.f = f
|
||||||
|
self.g = g
|
||||||
|
self.dg = nd.Derivative(g) if dg is None else dg
|
||||||
|
self.basis = basis
|
||||||
|
self.a = a
|
||||||
|
self.b = b
|
||||||
|
self.s = s
|
||||||
|
self.endpoints = endpoints
|
||||||
|
self.maxiter = maxiter
|
||||||
|
self.precision = precision
|
||||||
|
self.full_output = full_output
|
||||||
|
|
||||||
|
def _change_interval_to_0_1(self, f, g, dg, a, b):
|
||||||
|
def f1(t, *args, **kwds):
|
||||||
|
den = 1-t
|
||||||
|
return f(a + t / den, *args, **kwds) / den ** 2
|
||||||
|
|
||||||
|
def g1(t, *args, **kwds):
|
||||||
|
return g(a + t / (1 - t), *args, **kwds)
|
||||||
|
|
||||||
|
def dg1(t, *args, **kwds):
|
||||||
|
den = 1-t
|
||||||
|
return dg(a + t / den, *args, **kwds) / den ** 2
|
||||||
|
return f1, g2, dg1, 0., 1.
|
||||||
|
|
||||||
|
def _change_interval_to_m1_0(self, f, g, dg, a, b):
|
||||||
|
def f2(t, *args, **kwds):
|
||||||
|
den = 1 + t
|
||||||
|
return f(b + t / den, *args, **kwds) / den ** 2
|
||||||
|
|
||||||
|
def g2(t, *args, **kwds):
|
||||||
|
return g(b + t / (1 + t), *args, **kwds)
|
||||||
|
|
||||||
|
def dg2(t, *args, **kwds):
|
||||||
|
den = 1 + t
|
||||||
|
return dg(b + t / den, *args, **kwds) / den ** 2
|
||||||
|
return f2, g2, dg2, -1.0, 0.0
|
||||||
|
|
||||||
|
def _change_interval_to_m1_1(self, f, g, dg, a, b):
|
||||||
|
def f2(t, *args, **kwds):
|
||||||
|
den = (1 - t**2)
|
||||||
|
return f(t / den, *args, **kwds) * (1+t**2) / den ** 2
|
||||||
|
|
||||||
|
def g2(t, *args, **kwds):
|
||||||
|
den = (1 - t**2)
|
||||||
|
return g(t / den, *args, **kwds)
|
||||||
|
|
||||||
|
def dg2(t, *args, **kwds):
|
||||||
|
den = (1 - t**2)
|
||||||
|
return dg(t / den, *args, **kwds) * (1+t**2) / den ** 2
|
||||||
|
return f2, g2, dg2, -1., 1.
|
||||||
|
|
||||||
|
def _get_functions(self):
|
||||||
|
a, b = self.a, self.b
|
||||||
|
reverse = np.real(a) > np.real(b)
|
||||||
|
if reverse:
|
||||||
|
a, b = b, a
|
||||||
|
f, g, dg = self.f, self.g, self.dg
|
||||||
|
|
||||||
|
if a == b:
|
||||||
|
pass
|
||||||
|
elif np.isinf(a) | np.isinf(b):
|
||||||
|
# Check real limits
|
||||||
|
if ~np.isreal(a) | ~np.isreal(b) | np.isnan(a) | np.isnan(b):
|
||||||
|
raise ValueError('Infinite intervals must be real.')
|
||||||
|
# Change of variable
|
||||||
|
if np.isfinite(a) & np.isinf(b):
|
||||||
|
f, g, dg, a, b = self._change_interval_to_0_1(f, g, dg, a, b)
|
||||||
|
elif np.isinf(a) & np.isfinite(b):
|
||||||
|
f, g, dg, a, b = self._change_interval_to_m1_0(f, g, dg, a, b)
|
||||||
|
else: # -inf to inf
|
||||||
|
f, g, dg, a, b = self._change_interval_to_m1_1(f, g, dg, a, b)
|
||||||
|
|
||||||
|
return f, g, dg, a, b, reverse
|
||||||
|
|
||||||
|
def __call__(self, omega, *args, **kwds):
|
||||||
|
f, g, dg, a, b, reverse = self._get_functions()
|
||||||
|
|
||||||
|
Q, err = self._quad_osc(f, g, dg, a, b, omega, *args, **kwds)
|
||||||
|
|
||||||
|
if reverse:
|
||||||
|
Q = -Q
|
||||||
|
if self.full_output:
|
||||||
|
return Q, err
|
||||||
|
return Q
|
||||||
|
|
||||||
|
def _get_best_estimate(self, k, Q0, Q1, Q2):
|
||||||
|
if k >= 5:
|
||||||
|
Qv = np.hstack((Q0[k], Q1[k], Q2[k]))
|
||||||
|
Qw = np.hstack((Q0[k - 1], Q1[k - 1], Q2[k - 1]))
|
||||||
|
elif k >= 3:
|
||||||
|
Qv = np.hstack((Q0[k], Q1[k]))
|
||||||
|
Qw = np.hstack((Q0[k - 1], Q1[k - 1]))
|
||||||
|
else:
|
||||||
|
Qv = np.atleast_1d(Q0[k])
|
||||||
|
Qw = Q0[k - 1]
|
||||||
|
errors = np.atleast_1d(abs(Qv - Qw))
|
||||||
|
j = np.nanargmin(errors)
|
||||||
|
return Qv[j], errors[j]
|
||||||
|
|
||||||
|
def _extrapolate(self, k, Q0, Q1, Q2):
|
||||||
|
if k >= 4:
|
||||||
|
Q1[k], _err1 = dea3(Q0[k - 2], Q0[k - 1], Q0[k])
|
||||||
|
Q2[k], _err2 = dea3(Q1[k - 2], Q1[k - 1], Q1[k])
|
||||||
|
elif k >= 2:
|
||||||
|
Q1[k], _err1 = dea3(Q0[k - 2], Q0[k - 1], Q0[k])
|
||||||
|
# # Richardson extrapolation
|
||||||
|
# if k >= 4:
|
||||||
|
# Q1[k] = richardson(Q0, k)
|
||||||
|
# Q2[k] = richardson(Q1, k)
|
||||||
|
# elif k >= 2:
|
||||||
|
# Q1[k] = richardson(Q0, k)
|
||||||
|
Q, err = self._get_best_estimate(k, Q0, Q1, Q2)
|
||||||
|
return Q, err
|
||||||
|
|
||||||
|
def _quad_osc(self, f, g, dg, a, b, omega, *args, **kwds):
|
||||||
|
if a == b:
|
||||||
|
Q = b - a
|
||||||
|
err = b - a
|
||||||
|
return Q, err
|
||||||
|
|
||||||
|
abseps = 10**-self.precision
|
||||||
|
max_iter = self.maxiter
|
||||||
|
basis = self.basis
|
||||||
|
if self.endpoints:
|
||||||
|
xq = chebyshev_extrema(self.s)
|
||||||
|
else:
|
||||||
|
xq = chebyshev_roots(self.s)
|
||||||
|
# xq = tanh_sinh_open_nodes(self.s)
|
||||||
|
|
||||||
|
# One interval
|
||||||
|
hh = (b - a) / 2
|
||||||
|
x = (a + b) / 2 + hh * xq # Nodes
|
||||||
|
|
||||||
|
dtype = complex
|
||||||
|
Q0 = np.zeros((max_iter, 1), dtype=dtype) # Quadrature
|
||||||
|
Q1 = np.zeros((max_iter, 1), dtype=dtype) # First extrapolation
|
||||||
|
Q2 = np.zeros((max_iter, 1), dtype=dtype) # Second extrapolation
|
||||||
|
|
||||||
|
lim_f = Limit(f)
|
||||||
|
ab = np.hstack([a, b])
|
||||||
|
wq = osc_weights(omega, g, dg, xq, basis, ab, *args, **kwds)
|
||||||
|
Q0[0] = hh * np.sum(wq * lim_f(x, *args, **kwds))
|
||||||
|
|
||||||
|
# Successive bisection of intervals
|
||||||
|
nq = len(xq)
|
||||||
|
n = nq
|
||||||
|
for k in xrange(1, max_iter):
|
||||||
|
n += nq*2**k
|
||||||
|
|
||||||
|
hh = hh / 2
|
||||||
|
x = np.hstack([x + a, x + b]) / 2
|
||||||
|
ab = np.hstack([ab + a, ab + b]) / 2
|
||||||
|
wq = osc_weights(omega, g, dg, xq, basis, ab, *args, **kwds)
|
||||||
|
|
||||||
|
Q0[k] = hh * np.sum(wq * lim_f(x, *args, **kwds))
|
||||||
|
|
||||||
|
Q, err = self._extrapolate(k, Q0, Q1, Q2)
|
||||||
|
|
||||||
|
convergence = (err <= abseps) | ~np.isfinite(Q)
|
||||||
|
if convergence:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
warnings.warn('Max number of iterations reached '
|
||||||
|
'without convergence.')
|
||||||
|
|
||||||
|
if ~np.isfinite(Q):
|
||||||
|
warnings.warn('Integral approximation is Infinite or NaN.')
|
||||||
|
|
||||||
|
# The error estimate should not be zero
|
||||||
|
err += 2 * np.finfo(Q).eps
|
||||||
|
return Q, self.info(err, n)
|
||||||
|
|
||||||
|
|
||||||
|
def adaptive_levin_points(M, delta):
|
||||||
|
m = M - 1
|
||||||
|
prm = 0.5
|
||||||
|
while prm * m / delta >= 1:
|
||||||
|
delta = 2 * delta
|
||||||
|
k = np.arange(M)
|
||||||
|
x = piecewise([k < prm * m, k == np.ceil(prm * m)],
|
||||||
|
[-1 + k / delta, 0 * k, 1 - (m - k) / delta])
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def open_levin_points(M, delta):
|
||||||
|
return adaptive_levin_points(M+2, delta)[1:-1]
|
||||||
|
|
||||||
|
|
||||||
|
def chebyshev_extrema(M, delta=None):
|
||||||
|
k = np.arange(M)
|
||||||
|
x = np.cos(k * np.pi / (M-1))
|
||||||
|
return x
|
||||||
|
|
||||||
|
_EPS = np.finfo(float).eps
|
||||||
|
|
||||||
|
|
||||||
|
def tanh_sinh_nodes(M, delta=None, tol=_EPS):
|
||||||
|
tmax = np.arcsinh(np.arctanh(1-_EPS)*2/np.pi)
|
||||||
|
# tmax = 3.18
|
||||||
|
m = int(np.floor(-np.log2(tmax/max(M-1, 1)))) - 1
|
||||||
|
h = 2.0**-m
|
||||||
|
t = np.arange((M+1)//2+1)*h
|
||||||
|
x = np.tanh(np.pi/2*np.sinh(t))
|
||||||
|
k = np.flatnonzero(np.abs(x - 1) <= 10*tol)
|
||||||
|
y = x[:k[0]+1] if len(k) else x
|
||||||
|
return np.hstack((-y[:0:-1], y))
|
||||||
|
|
||||||
|
|
||||||
|
def tanh_sinh_open_nodes(M, delta=None, tol=_EPS):
|
||||||
|
return tanh_sinh_nodes(M+1, delta, tol)[1:-1]
|
||||||
|
|
||||||
|
|
||||||
|
def chebyshev_roots(M, delta=None):
|
||||||
|
k = np.arange(1, 2*M, 2) * 0.5
|
||||||
|
x = np.cos(k * np.pi / M)
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
class AdaptiveLevin(object):
|
||||||
|
'''Return integral for the Levin-type and adaptive Levin-type methods'''
|
||||||
|
info = namedtuple('info', ['error_estimate', 'n'])
|
||||||
|
|
||||||
|
def __init__(self, f, g, dg=None, a=-1, b=1, basis=chebyshev_basis, s=1,
|
||||||
|
precision=10, endpoints=True, full_output=False):
|
||||||
|
self.f = f
|
||||||
|
self.g = g
|
||||||
|
self.dg = nd.Derivative(g) if dg is None else dg
|
||||||
|
self.basis = basis
|
||||||
|
self.a = a
|
||||||
|
self.b = b
|
||||||
|
self.s = s
|
||||||
|
self.precision = precision
|
||||||
|
self.endpoints = endpoints
|
||||||
|
self.full_output = full_output
|
||||||
|
|
||||||
|
def aLevinTQ(self, omega, ff, gg, dgg, x, s, basis, *args, **kwds):
|
||||||
|
|
||||||
|
def Psi(t, k):
|
||||||
|
return dgg(t, *args, **kwds) * basis(t, k)
|
||||||
|
|
||||||
|
j_w = 1j * omega
|
||||||
|
nu = np.ones((len(x),), dtype=int)
|
||||||
|
nu[0] = nu[-1] = s
|
||||||
|
S = np.cumsum(np.hstack((nu, 0)))
|
||||||
|
S[-1] = 0
|
||||||
|
nn = int(S[-2])
|
||||||
|
A = np.zeros((nn, nn), dtype=complex)
|
||||||
|
F = np.zeros((nn,))
|
||||||
|
dff = Limit(nda.Derivative(ff))
|
||||||
|
dPsi = Limit(nda.Derivative(Psi))
|
||||||
|
dbasis = basis.derivative
|
||||||
|
for r, t in enumerate(x):
|
||||||
|
for j in range(S[r - 1], S[r]):
|
||||||
|
order = ((j - S[r - 1]) % nu[r]) # derivative order
|
||||||
|
dff.f.n = order
|
||||||
|
F[j] = dff(t, *args, **kwds)
|
||||||
|
dPsi.f.n = order
|
||||||
|
for k in range(nn):
|
||||||
|
A[j, k] = (dbasis(t, k, n=order+1) + j_w * dPsi(t, k))
|
||||||
|
k1 = np.flatnonzero(1-np.isfinite(F))
|
||||||
|
if k1.size > 0: # Remove singularities
|
||||||
|
warnings.warn('Singularities detected! ')
|
||||||
|
A[k1] = 0
|
||||||
|
F[k1] = 0
|
||||||
|
LS = linalg.lstsq(A, F)
|
||||||
|
v = basis.eval([-1, 1], LS[0])
|
||||||
|
|
||||||
|
lim_gg = Limit(gg)
|
||||||
|
gb = np.exp(j_w * lim_gg(1, *args, **kwds))
|
||||||
|
if np.isnan(gb):
|
||||||
|
gb = 0
|
||||||
|
ga = np.exp(j_w * lim_gg(-1, *args, **kwds))
|
||||||
|
if np.isnan(ga):
|
||||||
|
ga = 0
|
||||||
|
NR = (v[1] * gb - v[0] * ga)
|
||||||
|
return NR
|
||||||
|
|
||||||
|
def _get_integration_limits(self, omega, args, kwds):
|
||||||
|
a, b = self.a, self.b
|
||||||
|
M = 30
|
||||||
|
ab = [a]
|
||||||
|
scale = (b - a) / 2
|
||||||
|
n = 30
|
||||||
|
x = np.linspace(a, b, n + 1)
|
||||||
|
dg_x = np.asarray([scale * omega * self.dg(xi, *args, **kwds)
|
||||||
|
for xi in x])
|
||||||
|
i10 = findcross(dg_x, M)
|
||||||
|
i1 = findcross(dg_x, 1)
|
||||||
|
i0 = findcross(dg_x, 0)
|
||||||
|
im1 = findcross(dg_x, -1)
|
||||||
|
im10 = findcross(dg_x, -M)
|
||||||
|
x10 = ecross(x, dg_x, i10, M) if len(i10) else ()
|
||||||
|
x1 = ecross(x, dg_x, i1, 1) if len(i1) else ()
|
||||||
|
x0 = ecross(x, dg_x, i0, 0) if len(i0) else ()
|
||||||
|
xm1 = ecross(x, dg_x, im1, -1) if len(im1) else ()
|
||||||
|
xm10 = ecross(x, dg_x, im10, -M) if len(im10) else ()
|
||||||
|
|
||||||
|
for i in np.unique(np.hstack((x10, x1, x0, xm1, xm10))):
|
||||||
|
if x[0] < i < x[n]:
|
||||||
|
ab.append(i)
|
||||||
|
ab.append(b)
|
||||||
|
return ab
|
||||||
|
|
||||||
|
def __call__(self, omega, *args, **kwds):
|
||||||
|
ab = self._get_integration_limits(omega, args, kwds)
|
||||||
|
s = self.s
|
||||||
|
val = 0
|
||||||
|
n = 0
|
||||||
|
err = 0
|
||||||
|
for ai, bi in zip(ab[:-1], ab[1:]):
|
||||||
|
vali, infoi = self._QaL(s, ai, bi, omega, *args, **kwds)
|
||||||
|
val += vali
|
||||||
|
err += infoi.error_estimate
|
||||||
|
n += infoi.n
|
||||||
|
if self.full_output:
|
||||||
|
info = self.info(err, n)
|
||||||
|
return val, info
|
||||||
|
return val
|
||||||
|
|
||||||
|
def _get_num_points(self, s, prec, betam):
|
||||||
|
return 1 if s > 1 else int(prec / max(np.log10(betam + 1), 1) + 1)
|
||||||
|
|
||||||
|
def _QaL(self, s, a, b, omega, *args, **kwds):
|
||||||
|
'''if s>1,the integral is computed by Q_s^L'''
|
||||||
|
scale = (b - a) / 2
|
||||||
|
offset = (a + b) / 2
|
||||||
|
prec = self.precision # desired precision
|
||||||
|
|
||||||
|
def ff(t, *args, **kwds):
|
||||||
|
return scale * self.f(scale * t + offset, *args, **kwds)
|
||||||
|
|
||||||
|
def gg(t, *args, **kwds):
|
||||||
|
return self.g(scale * t + offset, *args, **kwds)
|
||||||
|
|
||||||
|
def dgg(t, *args, **kwds):
|
||||||
|
return scale * self.dg(scale * t + offset, *args, **kwds)
|
||||||
|
dg_a = abs(omega * dgg(-1, *args, **kwds))
|
||||||
|
dg_b = abs(omega * dgg(1, *args, **kwds))
|
||||||
|
g_a = abs(omega * gg(-1, *args, **kwds))
|
||||||
|
g_b = abs(omega * gg(1, *args, **kwds))
|
||||||
|
delta, alpha = min(dg_a, dg_b), min(g_a, g_b)
|
||||||
|
|
||||||
|
betam = delta # * scale
|
||||||
|
if self.endpoints:
|
||||||
|
if (delta < 10 or alpha <= 10 or s > 1):
|
||||||
|
points = chebyshev_extrema
|
||||||
|
else:
|
||||||
|
points = adaptive_levin_points
|
||||||
|
elif (delta < 10 or alpha <= 10 or s > 1):
|
||||||
|
points = chebyshev_roots
|
||||||
|
else:
|
||||||
|
points = open_levin_points # tanh_sinh_open_nodes
|
||||||
|
|
||||||
|
m = self._get_num_points(s, prec, betam)
|
||||||
|
abseps = 10*10.0**-prec
|
||||||
|
num_collocation_point_list = m*2**np.arange(1, 5) + 1
|
||||||
|
basis = self.basis
|
||||||
|
|
||||||
|
Q = 1e+300
|
||||||
|
n = 0
|
||||||
|
ni = 0
|
||||||
|
for num_collocation_points in num_collocation_point_list:
|
||||||
|
ni_old = ni
|
||||||
|
Q_old = Q
|
||||||
|
x = points(num_collocation_points, betam)
|
||||||
|
ni = len(x)
|
||||||
|
if ni > ni_old:
|
||||||
|
Q = self.aLevinTQ(omega, ff, gg, dgg, x, s, basis, *args,
|
||||||
|
**kwds)
|
||||||
|
n += ni
|
||||||
|
err = np.abs(Q-Q_old)
|
||||||
|
if err <= abseps:
|
||||||
|
break
|
||||||
|
info = self.info(err, n)
|
||||||
|
return Q, info
|
||||||
|
|
||||||
|
|
||||||
|
class EvansWebster(AdaptiveLevin):
|
||||||
|
'''Return integral for the Evans Webster method'''
|
||||||
|
|
||||||
|
def __init__(self, f, g, dg=None, a=-1, b=1, basis=chebyshev_basis, s=8,
|
||||||
|
precision=10, endpoints=False, full_output=False):
|
||||||
|
self.f = f
|
||||||
|
self.g = g
|
||||||
|
self.dg = nd.Derivative(g) if dg is None else dg
|
||||||
|
self.basis = basis
|
||||||
|
self.a = a
|
||||||
|
self.b = b
|
||||||
|
self.s = s
|
||||||
|
self.endpoints = endpoints
|
||||||
|
self.precision = precision
|
||||||
|
self.full_output = full_output
|
||||||
|
|
||||||
|
def aLevinTQ(self, omega, ff, gg, dgg, x, s, basis, *args, **kwds):
|
||||||
|
w = evans_webster_weights(omega, gg, dgg, x, basis, *args, **kwds)
|
||||||
|
|
||||||
|
f = Limit(ff)(x, *args, **kwds)
|
||||||
|
NR = np.sum(f*w)
|
||||||
|
return NR
|
||||||
|
|
||||||
|
def _get_num_points(self, s, prec, betam):
|
||||||
|
return 8 if s > 1 else int(prec / max(np.log10(betam + 1), 1) + 1)
|
||||||
|
|
||||||
|
# def _QaL(self, s, a, b, omega, *args, **kwds):
|
||||||
|
# '''if s>1,the integral is computed by Q_s^L'''
|
||||||
|
# scale = (b - a) / 2
|
||||||
|
# offset = (a + b) / 2
|
||||||
|
# prec = self.precision # desired precision
|
||||||
|
#
|
||||||
|
# def ff(t, *args, **kwds):
|
||||||
|
# return scale * self.f(scale * t + offset, *args, **kwds)
|
||||||
|
#
|
||||||
|
# def gg(t, *args, **kwds):
|
||||||
|
# return self.g(scale * t + offset, *args, **kwds)
|
||||||
|
#
|
||||||
|
# def dgg(t, *args, **kwds):
|
||||||
|
# return scale * self.dg(scale * t + offset, *args, **kwds)
|
||||||
|
# dg_a = abs(omega * dgg(-1, *args, **kwds))
|
||||||
|
# dg_b = abs(omega * dgg(1, *args, **kwds))
|
||||||
|
# g_a = abs(omega * gg(-1, *args, **kwds))
|
||||||
|
# g_b = abs(omega * gg(1, *args, **kwds))
|
||||||
|
# delta, alpha = min(dg_a, dg_b), min(g_a, g_b)
|
||||||
|
#
|
||||||
|
# betam = delta # * scale
|
||||||
|
# if self.endpoints:
|
||||||
|
# if (delta < 10 or alpha <= 10 or s > 1):
|
||||||
|
# points = chebyshev_extrema
|
||||||
|
# else:
|
||||||
|
# points = adaptive_levin_points
|
||||||
|
# elif (delta < 10 or alpha <= 10 or s > 1):
|
||||||
|
# points = chebyshev_roots
|
||||||
|
# else:
|
||||||
|
# points = tanh_sinh_open_nodes
|
||||||
|
#
|
||||||
|
# m = 8 if s > 1 else int(prec / max(np.log10(betam + 1), 1) + 1)
|
||||||
|
# abseps = 10*10.0**-prec
|
||||||
|
# num_collocation_point_list = 2*m*np.arange(1, 6, 2)
|
||||||
|
# # range(num_points, num_points+3, 2)
|
||||||
|
# basis = self.basis
|
||||||
|
# Q = 1e+300
|
||||||
|
# n = 0
|
||||||
|
# for num_collocation_points in num_collocation_point_list:
|
||||||
|
# Q_old = Q
|
||||||
|
# x = points(num_collocation_points-1, betam)
|
||||||
|
# Q = self.aLevinTQ(omega, ff, gg, dgg, x, s, basis, *args, **kwds)
|
||||||
|
# n += num_collocation_points
|
||||||
|
# err = np.abs(Q-Q_old)
|
||||||
|
# if err <= abseps:
|
||||||
|
# break
|
||||||
|
# info = self.info(err, n)
|
||||||
|
# return Q, info
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
tanh_sinh_nodes(16)
|
@ -1,144 +0,0 @@
|
|||||||
from operator import itemgetter as _itemgetter
|
|
||||||
from keyword import iskeyword as _iskeyword
|
|
||||||
import sys as _sys
|
|
||||||
|
|
||||||
|
|
||||||
def namedtuple(typename, field_names, verbose=False):
|
|
||||||
"""Returns a new subclass of tuple with named fields.
|
|
||||||
|
|
||||||
>>> Point = namedtuple('Point', 'x y')
|
|
||||||
>>> Point.__doc__ # docstring for the new class
|
|
||||||
'Point(x, y)'
|
|
||||||
>>> p = Point(11, y=22) # instantiate with positional args or keywords
|
|
||||||
>>> p[0] + p[1] # indexable like a plain tuple
|
|
||||||
33
|
|
||||||
>>> x, y = p # unpack like a regular tuple
|
|
||||||
>>> x, y
|
|
||||||
(11, 22)
|
|
||||||
>>> p.x + p.y # fields also accessable by name
|
|
||||||
33
|
|
||||||
>>> d = p._asdict() # convert to a dictionary
|
|
||||||
>>> d['x']
|
|
||||||
11
|
|
||||||
>>> Point(**d) # convert from a dictionary
|
|
||||||
Point(x=11, y=22)
|
|
||||||
>>> p._replace(x=100) # _replace() is like str.replace() but targets named fields
|
|
||||||
Point(x=100, y=22)
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Parse and validate the field names. Validation serves two purposes,
|
|
||||||
# generating informative error messages and preventing template injection
|
|
||||||
# attacks.
|
|
||||||
if isinstance(field_names, basestring):
|
|
||||||
# names separated by whitespace and/or commas
|
|
||||||
field_names = field_names.replace(',', ' ').split()
|
|
||||||
field_names = tuple(field_names)
|
|
||||||
for name in (typename,) + field_names:
|
|
||||||
if not min(c.isalnum() or c == '_' for c in name):
|
|
||||||
raise ValueError(
|
|
||||||
'Type names and field names can only contain alphanumeric ' +
|
|
||||||
'characters and underscores: %r' % name)
|
|
||||||
if _iskeyword(name):
|
|
||||||
raise ValueError(
|
|
||||||
'Type names and field names cannot be a keyword: %r' % name)
|
|
||||||
if name[0].isdigit():
|
|
||||||
raise ValueError('Type names and field names cannot start ' +
|
|
||||||
'with a number: %r' % name)
|
|
||||||
seen_names = set()
|
|
||||||
for name in field_names:
|
|
||||||
if name.startswith('_'):
|
|
||||||
raise ValueError(
|
|
||||||
'Field names cannot start with an underscore: %r' % name)
|
|
||||||
if name in seen_names:
|
|
||||||
raise ValueError('Encountered duplicate field name: %r' % name)
|
|
||||||
seen_names.add(name)
|
|
||||||
|
|
||||||
# Create and fill-in the class template
|
|
||||||
numfields = len(field_names)
|
|
||||||
# tuple repr without parens or quotes
|
|
||||||
argtxt = repr(field_names).replace("'", "")[1:-1]
|
|
||||||
reprtxt = ', '.join('%s=%%r' % name for name in field_names)
|
|
||||||
dicttxt = ', '.join('%r: t[%d]' % (name, pos)
|
|
||||||
for pos, name in enumerate(field_names))
|
|
||||||
template = '''class %(typename)s(tuple):
|
|
||||||
'%(typename)s(%(argtxt)s)' \n
|
|
||||||
__slots__ = () \n
|
|
||||||
_fields = %(field_names)r \n
|
|
||||||
def __new__(cls, %(argtxt)s):
|
|
||||||
return tuple.__new__(cls, (%(argtxt)s)) \n
|
|
||||||
@classmethod
|
|
||||||
def _make(cls, iterable, new=tuple.__new__, len=len):
|
|
||||||
'Make a new %(typename)s object from a sequence or iterable'
|
|
||||||
result = new(cls, iterable)
|
|
||||||
if len(result) != %(numfields)d:
|
|
||||||
raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))
|
|
||||||
return result \n
|
|
||||||
def __repr__(self):
|
|
||||||
return '%(typename)s(%(reprtxt)s)' %% self \n
|
|
||||||
def _asdict(t):
|
|
||||||
'Return a new dict which maps field names to their values'
|
|
||||||
return {%(dicttxt)s} \n
|
|
||||||
def _replace(self, **kwds):
|
|
||||||
'Return a new %(typename)s object replacing specified fields with new values'
|
|
||||||
result = self._make(map(kwds.pop, %(field_names)r, self))
|
|
||||||
if kwds:
|
|
||||||
raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
|
|
||||||
return result \n\n''' % locals()
|
|
||||||
for i, name in enumerate(field_names):
|
|
||||||
template += ' %s = property(itemgetter(%d))\n' % (name, i)
|
|
||||||
if verbose:
|
|
||||||
print template
|
|
||||||
|
|
||||||
# Execute the template string in a temporary namespace
|
|
||||||
namespace = dict(itemgetter=_itemgetter)
|
|
||||||
try:
|
|
||||||
exec template in namespace
|
|
||||||
except SyntaxError, e:
|
|
||||||
raise SyntaxError(e.message + ':\n' + template)
|
|
||||||
result = namespace[typename]
|
|
||||||
|
|
||||||
# For pickling to work, the __module__ variable needs to be set to the
|
|
||||||
# frame where the named tuple is created. Bypass this step in enviroments
|
|
||||||
# where sys._getframe is not defined (Jython for example).
|
|
||||||
if hasattr(_sys, '_getframe'):
|
|
||||||
result.__module__ = _sys._getframe(1).f_globals['__name__']
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# verify that instances can be pickled
|
|
||||||
from cPickle import loads, dumps
|
|
||||||
Point = namedtuple('Point', 'x, y', True)
|
|
||||||
p = Point(x=10, y=20)
|
|
||||||
assert p == loads(dumps(p))
|
|
||||||
|
|
||||||
# test and demonstrate ability to override methods
|
|
||||||
class Point(namedtuple('Point', 'x y')):
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hypot(self):
|
|
||||||
return (self.x ** 2 + self.y ** 2) ** 0.5
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y,
|
|
||||||
self.hypot)
|
|
||||||
|
|
||||||
for p in Point(3, 4), Point(14, 5), Point(9. / 7, 6):
|
|
||||||
print(p)
|
|
||||||
|
|
||||||
class Point(namedtuple('Point', 'x y')):
|
|
||||||
'''Point class with optimized _make() and _replace()
|
|
||||||
without error-checking
|
|
||||||
'''
|
|
||||||
_make = classmethod(tuple.__new__)
|
|
||||||
|
|
||||||
def _replace(self, _map=map, **kwds):
|
|
||||||
return self._make(_map(kwds.get, ('x', 'y'), self))
|
|
||||||
|
|
||||||
print(Point(11, 22)._replace(x=100))
|
|
||||||
|
|
||||||
import doctest
|
|
||||||
TestResults = namedtuple('TestResults', 'failed attempted')
|
|
||||||
print(TestResults(*doctest.testmod()))
|
|
@ -0,0 +1,387 @@
|
|||||||
|
'''
|
||||||
|
Created on 31. aug. 2015
|
||||||
|
|
||||||
|
@author: pab
|
||||||
|
'''
|
||||||
|
from __future__ import division
|
||||||
|
import numpy as np
|
||||||
|
import mpmath as mp
|
||||||
|
import unittest
|
||||||
|
from wafo.integrate_oscillating import (adaptive_levin_points,
|
||||||
|
chebyshev_extrema,
|
||||||
|
chebyshev_roots, tanh_sinh_nodes,
|
||||||
|
tanh_sinh_open_nodes,
|
||||||
|
AdaptiveLevin, poly_basis,
|
||||||
|
chebyshev_basis,
|
||||||
|
EvansWebster, QuadOsc)
|
||||||
|
# import numdifftools as nd
|
||||||
|
from numpy.testing import assert_allclose
|
||||||
|
from scipy.special import gamma, digamma
|
||||||
|
_EPS = np.finfo(float).eps
|
||||||
|
|
||||||
|
|
||||||
|
class TestBasis(unittest.TestCase):
|
||||||
|
def test_poly(self):
|
||||||
|
t = 1
|
||||||
|
vals = [poly_basis.derivative(t, k, n=1) for k in range(3)]
|
||||||
|
assert_allclose(vals, range(3))
|
||||||
|
vals = [poly_basis.derivative(0, k, n=1) for k in range(3)]
|
||||||
|
assert_allclose(vals, [0, 1, 0])
|
||||||
|
vals = [poly_basis.derivative(0, k, n=2) for k in range(3)]
|
||||||
|
assert_allclose(vals, [0, 0, 2])
|
||||||
|
|
||||||
|
def test_chebyshev(self):
|
||||||
|
t = 1
|
||||||
|
vals = [chebyshev_basis.derivative(t, k, n=1) for k in range(3)]
|
||||||
|
assert_allclose(vals, np.arange(3)**2)
|
||||||
|
vals = [chebyshev_basis.derivative(0, k, n=1) for k in range(3)]
|
||||||
|
assert_allclose(vals, [0, 1, 0])
|
||||||
|
vals = [chebyshev_basis.derivative(0, k, n=2) for k in range(3)]
|
||||||
|
assert_allclose(vals, [0, 0, 4])
|
||||||
|
|
||||||
|
|
||||||
|
class TestLevinPoints(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_adaptive(self):
|
||||||
|
M = 11
|
||||||
|
delta = 100
|
||||||
|
x = adaptive_levin_points(M, delta)
|
||||||
|
true_x = [-1., -0.99, -0.98, -0.97, -0.96, 0.,
|
||||||
|
0.96, 0.97, 0.98, 0.99, 1.]
|
||||||
|
assert_allclose(x, true_x)
|
||||||
|
|
||||||
|
def test_chebyshev_extrema(self):
|
||||||
|
M = 11
|
||||||
|
delta = 100
|
||||||
|
x = chebyshev_extrema(M, delta)
|
||||||
|
true_x = [1.000000e+00, 9.510565e-01, 8.090170e-01, 5.877853e-01,
|
||||||
|
3.090170e-01, 6.123234e-17, -3.090170e-01, -5.877853e-01,
|
||||||
|
-8.090170e-01, -9.510565e-01, -1.000000e+00]
|
||||||
|
assert_allclose(x, true_x)
|
||||||
|
|
||||||
|
def test_chebyshev_roots(self):
|
||||||
|
M = 11
|
||||||
|
delta = 100
|
||||||
|
x = chebyshev_roots(M, delta)
|
||||||
|
|
||||||
|
true_x = [9.89821442e-01, 9.09631995e-01, 7.55749574e-01,
|
||||||
|
5.40640817e-01, 2.81732557e-01, 2.83276945e-16,
|
||||||
|
-2.81732557e-01, -5.40640817e-01, -7.55749574e-01,
|
||||||
|
-9.09631995e-01, -9.89821442e-01]
|
||||||
|
assert_allclose(x, true_x)
|
||||||
|
|
||||||
|
def test_tanh_sinh_nodes(self):
|
||||||
|
for n in 2**np.arange(1, 5) + 1:
|
||||||
|
x = tanh_sinh_nodes(n)
|
||||||
|
# self.assertEqual(n, len(x))
|
||||||
|
|
||||||
|
def test_tanh_sinh_open_nodes(self):
|
||||||
|
for n in 2**np.arange(1, 5) + 1:
|
||||||
|
x = tanh_sinh_open_nodes(n)
|
||||||
|
# self.assertEqual(n, len(x))
|
||||||
|
|
||||||
|
|
||||||
|
class LevinQuadrature(unittest.TestCase):
|
||||||
|
def test_exp_4t_exp_jw_gamma_t_exp_4t(self):
|
||||||
|
def f(t):
|
||||||
|
return np.exp(4 * t) # amplitude function
|
||||||
|
|
||||||
|
def g(t):
|
||||||
|
return t + np.exp(4 * t) * gamma(t) # phase function
|
||||||
|
|
||||||
|
def dg(t):
|
||||||
|
return 1 + (4 + digamma(t)) * np.exp(4 * t) * gamma(t)
|
||||||
|
a = 1
|
||||||
|
b = 2
|
||||||
|
omega = 100
|
||||||
|
|
||||||
|
def ftot(t):
|
||||||
|
exp4t = mp.exp(4*t)
|
||||||
|
return exp4t * mp.exp(1j * omega * (t+exp4t*mp.gamma(t)))
|
||||||
|
|
||||||
|
_true_val, _err = mp.quadts(ftot, [a, (a+b)/2, b], error=True)
|
||||||
|
|
||||||
|
true_val = 0.00435354129735323908804 + 0.00202865398517716214366j
|
||||||
|
# quad = AdaptiveLevin(f, g, dg, a=a, b=b, s=1, full_output=True)
|
||||||
|
for quadfun in [EvansWebster, QuadOsc, AdaptiveLevin]:
|
||||||
|
quad = quadfun(f, g, dg, a=a, b=b, full_output=True)
|
||||||
|
val, info = quad(omega)
|
||||||
|
assert_allclose(val, true_val)
|
||||||
|
self.assert_(info.error_estimate < 1e-11)
|
||||||
|
# assert_allclose(info.n, 9)
|
||||||
|
|
||||||
|
def test_exp_jw_t(self):
|
||||||
|
def g(t):
|
||||||
|
return t
|
||||||
|
|
||||||
|
def dg(t):
|
||||||
|
return np.ones(np.shape(t))
|
||||||
|
|
||||||
|
def true_F(t):
|
||||||
|
return np.exp(1j*omega*g(t))/(1j*omega)
|
||||||
|
|
||||||
|
val, _err = mp.quadts(g, [0, 1], error=True)
|
||||||
|
a = 1
|
||||||
|
b = 2
|
||||||
|
omega = 1
|
||||||
|
true_val = true_F(b)-true_F(a)
|
||||||
|
|
||||||
|
for quadfun in [QuadOsc, AdaptiveLevin, EvansWebster]:
|
||||||
|
quad = quadfun(dg, g, dg, a, b, full_output=True)
|
||||||
|
val, info = quad(omega)
|
||||||
|
|
||||||
|
assert_allclose(val, true_val)
|
||||||
|
self.assert_(info.error_estimate < 1e-12)
|
||||||
|
# assert_allclose(info.n, 21)
|
||||||
|
|
||||||
|
def test_I1_1_p_ln_x_exp_jw_xlnx(self):
|
||||||
|
def g(t):
|
||||||
|
return t*np.log(t)
|
||||||
|
|
||||||
|
def dg(t):
|
||||||
|
return 1 + np.log(t)
|
||||||
|
|
||||||
|
def true_F(t):
|
||||||
|
return np.exp(1j*(omega*g(t)))/(1j*omega)
|
||||||
|
|
||||||
|
a = 100
|
||||||
|
b = 200
|
||||||
|
omega = 1
|
||||||
|
true_val = true_F(b)-true_F(a)
|
||||||
|
for quadfun in [AdaptiveLevin, QuadOsc, EvansWebster]:
|
||||||
|
quad = quadfun(dg, g, dg, a, b, full_output=True)
|
||||||
|
|
||||||
|
val, info = quad(omega)
|
||||||
|
|
||||||
|
assert_allclose(val, true_val)
|
||||||
|
self.assert_(info.error_estimate < 1e-10)
|
||||||
|
# assert_allclose(info.n, 11)
|
||||||
|
|
||||||
|
def test_I4_ln_x_exp_jw_30x(self):
|
||||||
|
n = 7
|
||||||
|
|
||||||
|
def g(t):
|
||||||
|
return t**n
|
||||||
|
|
||||||
|
def dg(t):
|
||||||
|
return n*t**(n-1)
|
||||||
|
|
||||||
|
def f(t):
|
||||||
|
return dg(t)*np.log(g(t))
|
||||||
|
|
||||||
|
a = 0
|
||||||
|
b = (2 * np.pi)**(1./n)
|
||||||
|
omega = 30
|
||||||
|
|
||||||
|
def ftot(t):
|
||||||
|
return n*t**(n-1)*mp.log(t**n) * mp.exp(1j * omega * t**n)
|
||||||
|
|
||||||
|
_true_val, _err = mp.quadts(ftot, [a, b], error=True, maxdegree=8)
|
||||||
|
# true_val = (-0.052183048684992 - 0.193877275099872j)
|
||||||
|
true_val = (-0.0521830486849921 - 0.193877275099871j)
|
||||||
|
|
||||||
|
for quadfun in [QuadOsc, EvansWebster, AdaptiveLevin]:
|
||||||
|
quad = quadfun(f, g, dg, a, b, full_output=True)
|
||||||
|
val, info = quad(omega)
|
||||||
|
assert_allclose(val, true_val)
|
||||||
|
self.assert_(info.error_estimate < 1e-5)
|
||||||
|
|
||||||
|
def test_I5_coscost_sint_exp_jw_sint(self):
|
||||||
|
a = 0
|
||||||
|
b = np.pi/2
|
||||||
|
omega = 100
|
||||||
|
|
||||||
|
def f(t):
|
||||||
|
return np.cos(np.cos(t))*np.sin(t)
|
||||||
|
|
||||||
|
def g(t):
|
||||||
|
return np.sin(t)
|
||||||
|
|
||||||
|
def dg(t):
|
||||||
|
return np.cos(t)
|
||||||
|
|
||||||
|
def ftot(t):
|
||||||
|
return mp.cos(mp.cos(t)) * mp.sin(t) * mp.exp(1j * omega *
|
||||||
|
mp.sin(t))
|
||||||
|
|
||||||
|
_true_val, _err = mp.quadts(ftot, [a, 0.5, 1, b], maxdegree=9,
|
||||||
|
error=True)
|
||||||
|
|
||||||
|
true_val = 0.0325497765499959-0.121009052128827j
|
||||||
|
for quadfun in [QuadOsc, EvansWebster, AdaptiveLevin]:
|
||||||
|
quad = quadfun(f, g, dg, a, b, full_output=True)
|
||||||
|
|
||||||
|
val, info = quad(omega)
|
||||||
|
|
||||||
|
assert_allclose(val, true_val)
|
||||||
|
self.assert_(info.error_estimate < 1e-9)
|
||||||
|
|
||||||
|
def test_I6_exp_jw_td_1_m_t(self):
|
||||||
|
a = 0
|
||||||
|
b = 1
|
||||||
|
omega = 1
|
||||||
|
|
||||||
|
def f(t):
|
||||||
|
return np.ones(np.shape(t))
|
||||||
|
|
||||||
|
def g(t):
|
||||||
|
return t/(1-t)
|
||||||
|
|
||||||
|
def dg(t):
|
||||||
|
return 1./(1-t)**2
|
||||||
|
|
||||||
|
def ftot(t):
|
||||||
|
return mp.exp(1j * omega * t/(1-t))
|
||||||
|
|
||||||
|
true_val = (0.3785503757641866423607342717846606761068353230802945830 +
|
||||||
|
0.3433779615564270328325330038583124340012440194999075192j)
|
||||||
|
for quadfun in [QuadOsc, EvansWebster, AdaptiveLevin]:
|
||||||
|
quad = quadfun(f, g, dg, a, b, endpoints=False, full_output=True)
|
||||||
|
|
||||||
|
val, info = quad(omega)
|
||||||
|
|
||||||
|
assert_allclose(val, true_val)
|
||||||
|
self.assert_(info.error_estimate < 1e-10)
|
||||||
|
|
||||||
|
def test_I8_cos_47pix2d4_exp_jw_x(self):
|
||||||
|
def f(t):
|
||||||
|
return np.cos(47*np.pi/4*t**2)
|
||||||
|
|
||||||
|
def g(t):
|
||||||
|
return t
|
||||||
|
|
||||||
|
def dg(t):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
a = -1
|
||||||
|
b = 1
|
||||||
|
omega = 451*np.pi/4
|
||||||
|
|
||||||
|
true_val = 2.3328690362927e-3
|
||||||
|
s = 15
|
||||||
|
for quadfun in [QuadOsc, EvansWebster]: # , AdaptiveLevin]:
|
||||||
|
quad = quadfun(f, g, dg, a, b, s=s, endpoints=False,
|
||||||
|
full_output=True)
|
||||||
|
val, _info = quad(omega)
|
||||||
|
assert_allclose(val.real, true_val)
|
||||||
|
s = 1 if s <= 2 else s // 2
|
||||||
|
# self.assert_(info.error_estimate < 1e-10)
|
||||||
|
# assert_allclose(info.n, 11)
|
||||||
|
|
||||||
|
def test_I9_exp_tant_sec2t_exp_jw_tant(self):
|
||||||
|
a = 0
|
||||||
|
b = np.pi/2
|
||||||
|
omega = 100
|
||||||
|
|
||||||
|
def f(t):
|
||||||
|
return np.exp(-np.tan(t))/np.cos(t)**2
|
||||||
|
|
||||||
|
def g(t):
|
||||||
|
return np.tan(t)
|
||||||
|
|
||||||
|
def dg(t):
|
||||||
|
return 1./np.cos(t)**2
|
||||||
|
|
||||||
|
true_val = (0.0000999900009999000099990000999900009999000099990000999 +
|
||||||
|
0.009999000099990000999900009999000099990000999900009999j)
|
||||||
|
for quadfun in [QuadOsc, EvansWebster, AdaptiveLevin]:
|
||||||
|
quad = quadfun(f, g, dg, a, b, endpoints=False, full_output=True)
|
||||||
|
|
||||||
|
val, info = quad(omega)
|
||||||
|
|
||||||
|
assert_allclose(val, true_val)
|
||||||
|
self.assert_(info.error_estimate < 1e-8)
|
||||||
|
|
||||||
|
def test_exp_zdcos2t_dcos2t_exp_jw_cos_t_b_dcos2t(self):
|
||||||
|
x1 = -20
|
||||||
|
y1 = 20
|
||||||
|
z1 = 20
|
||||||
|
beta = np.abs(np.arctan(y1/x1))
|
||||||
|
R = np.sqrt(x1**2+y1**2)
|
||||||
|
|
||||||
|
def f(t, beta, z1):
|
||||||
|
cos2t = np.cos(t)**2
|
||||||
|
return np.where(cos2t == 0, 0, np.exp(-z1/cos2t)/cos2t)
|
||||||
|
|
||||||
|
def g(t, beta, z1):
|
||||||
|
return np.cos(t-beta)/np.cos(t)**2
|
||||||
|
|
||||||
|
def dg(t, beta, z1=0):
|
||||||
|
cos3t = np.cos(t)**3
|
||||||
|
return 0.5*(3*np.sin(beta)-np.sin(beta-2*t))/cos3t
|
||||||
|
|
||||||
|
def append_dg_zero(zeros, g1, beta):
|
||||||
|
signs = [1, ] if np.abs(g1) <= _EPS else [-1, 1]
|
||||||
|
for sgn1 in signs:
|
||||||
|
tn = np.arccos(sgn1 * g1)
|
||||||
|
if -np.pi / 2 <= tn <= np.pi / 2:
|
||||||
|
for sgn2 in [-1, 1]:
|
||||||
|
t = sgn2 * tn
|
||||||
|
if np.abs(dg(t, beta)) < 10*_EPS:
|
||||||
|
zeros.append(t)
|
||||||
|
return zeros
|
||||||
|
|
||||||
|
def zeros_dg(beta):
|
||||||
|
k0 = (9*np.cos(2*beta)-7)
|
||||||
|
if k0 < 0: # No stationary points
|
||||||
|
return ()
|
||||||
|
k1 = 3*np.cos(2*beta)-5
|
||||||
|
g0 = np.sqrt(2)*np.sqrt(np.cos(beta)**2*k0)
|
||||||
|
zeros = []
|
||||||
|
|
||||||
|
if g0+k1 < _EPS:
|
||||||
|
g1 = 1./2*np.sqrt(-g0-k1)
|
||||||
|
zeros = append_dg_zero(zeros, g1, beta)
|
||||||
|
if _EPS < g0-k1:
|
||||||
|
g2 = 1./2*np.sqrt(g0-k1)
|
||||||
|
zeros = append_dg_zero(zeros, g2, beta)
|
||||||
|
if np.abs(g0+k1) <= _EPS or np.abs(g0-k1) <= _EPS:
|
||||||
|
zeros = append_dg_zero(zeros, 0, beta)
|
||||||
|
return tuple(zeros)
|
||||||
|
|
||||||
|
a = -np.pi/2
|
||||||
|
b = np.pi/2
|
||||||
|
omega = R
|
||||||
|
|
||||||
|
def ftot(t):
|
||||||
|
cos2t = mp.cos(t)**2
|
||||||
|
return (mp.exp(-z1/cos2t) / cos2t *
|
||||||
|
mp.exp(1j * omega * mp.cos(t-beta)/cos2t))
|
||||||
|
|
||||||
|
zdg = zeros_dg(beta)
|
||||||
|
ab = (a, ) + zdg + (b, )
|
||||||
|
true_val, _err = mp.quadts(ftot, ab, maxdegree=9, error=True)
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
t = np.linspace(a, b, 5*513)
|
||||||
|
plt.subplot(2, 1, 1)
|
||||||
|
f2 = f(t, beta, z1)*np.exp(1j*R*g(t, beta, z1))
|
||||||
|
|
||||||
|
true_val2 = np.trapz(f2, t)
|
||||||
|
plt.plot(t, f2.real, t, f2.imag, 'r')
|
||||||
|
plt.title('integral=%g+1j%g, '
|
||||||
|
'(%g+1j%g)' % (true_val2.real, true_val2.imag,
|
||||||
|
true_val.real, true_val.imag))
|
||||||
|
plt.subplot(2, 1, 2)
|
||||||
|
plt.plot(t, dg(t, beta, z1), 'r')
|
||||||
|
plt.plot(t, g(t, beta, z1))
|
||||||
|
plt.hlines(0, a, b)
|
||||||
|
plt.axis([a, b, -5, 5])
|
||||||
|
plt.title('beta=%g' % beta)
|
||||||
|
print(np.trapz(f2, t))
|
||||||
|
plt.show('hold')
|
||||||
|
# true_val = 0.00253186684281+0.004314054498j
|
||||||
|
# s = 15
|
||||||
|
for quadfun in [QuadOsc, EvansWebster, AdaptiveLevin]:
|
||||||
|
# EvansWebster]: # , AdaptiveLevin, ]:
|
||||||
|
quad = quadfun(f, g, dg, a, b, precision=10, endpoints=False,
|
||||||
|
full_output=True)
|
||||||
|
val, _info = quad(omega, beta, z1) # @UnusedVariable
|
||||||
|
# assert_allclose(val, complex(true_val), rtol=1e-3)
|
||||||
|
# s = 1 if s<=1 else s//2
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# import sys;sys.argv = ['', 'Test.testName']
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue