_pseudo_diffs.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. """
  2. Differential and pseudo-differential operators.
  3. """
  4. # Created by Pearu Peterson, September 2002
  5. __all__ = ['diff',
  6. 'tilbert','itilbert','hilbert','ihilbert',
  7. 'cs_diff','cc_diff','sc_diff','ss_diff',
  8. 'shift']
  9. import threading
  10. from numpy import pi, asarray, sin, cos, sinh, cosh, tanh, iscomplexobj
  11. from . import convolve
  12. from scipy.fft._pocketfft.helper import _datacopied
  13. _cache = threading.local()
  14. def diff(x,order=1,period=None, _cache=_cache):
  15. """
  16. Return kth derivative (or integral) of a periodic sequence x.
  17. If x_j and y_j are Fourier coefficients of periodic functions x
  18. and y, respectively, then::
  19. y_j = pow(sqrt(-1)*j*2*pi/period, order) * x_j
  20. y_0 = 0 if order is not 0.
  21. Parameters
  22. ----------
  23. x : array_like
  24. Input array.
  25. order : int, optional
  26. The order of differentiation. Default order is 1. If order is
  27. negative, then integration is carried out under the assumption
  28. that ``x_0 == 0``.
  29. period : float, optional
  30. The assumed period of the sequence. Default is ``2*pi``.
  31. Notes
  32. -----
  33. If ``sum(x, axis=0) = 0`` then ``diff(diff(x, k), -k) == x`` (within
  34. numerical accuracy).
  35. For odd order and even ``len(x)``, the Nyquist mode is taken zero.
  36. """
  37. if isinstance(_cache, threading.local):
  38. if not hasattr(_cache, 'diff_cache'):
  39. _cache.diff_cache = {}
  40. _cache = _cache.diff_cache
  41. tmp = asarray(x)
  42. if order == 0:
  43. return tmp
  44. if iscomplexobj(tmp):
  45. return diff(tmp.real, order, period, _cache)+1j*diff(
  46. tmp.imag, order, period, _cache)
  47. if period is not None:
  48. c = 2*pi/period
  49. else:
  50. c = 1.0
  51. n = len(x)
  52. omega = _cache.get((n,order,c))
  53. if omega is None:
  54. if len(_cache) > 20:
  55. while _cache:
  56. _cache.popitem()
  57. def kernel(k,order=order,c=c):
  58. if k:
  59. return pow(c*k,order)
  60. return 0
  61. omega = convolve.init_convolution_kernel(n,kernel,d=order,
  62. zero_nyquist=1)
  63. _cache[(n,order,c)] = omega
  64. overwrite_x = _datacopied(tmp, x)
  65. return convolve.convolve(tmp,omega,swap_real_imag=order % 2,
  66. overwrite_x=overwrite_x)
  67. def tilbert(x, h, period=None, _cache=_cache):
  68. """
  69. Return h-Tilbert transform of a periodic sequence x.
  70. If x_j and y_j are Fourier coefficients of periodic functions x
  71. and y, respectively, then::
  72. y_j = sqrt(-1)*coth(j*h*2*pi/period) * x_j
  73. y_0 = 0
  74. Parameters
  75. ----------
  76. x : array_like
  77. The input array to transform.
  78. h : float
  79. Defines the parameter of the Tilbert transform.
  80. period : float, optional
  81. The assumed period of the sequence. Default period is ``2*pi``.
  82. Returns
  83. -------
  84. tilbert : ndarray
  85. The result of the transform.
  86. Notes
  87. -----
  88. If ``sum(x, axis=0) == 0`` and ``n = len(x)`` is odd, then
  89. ``tilbert(itilbert(x)) == x``.
  90. If ``2 * pi * h / period`` is approximately 10 or larger, then
  91. numerically ``tilbert == hilbert``
  92. (theoretically oo-Tilbert == Hilbert).
  93. For even ``len(x)``, the Nyquist mode of ``x`` is taken zero.
  94. """
  95. if isinstance(_cache, threading.local):
  96. if not hasattr(_cache, 'tilbert_cache'):
  97. _cache.tilbert_cache = {}
  98. _cache = _cache.tilbert_cache
  99. tmp = asarray(x)
  100. if iscomplexobj(tmp):
  101. return tilbert(tmp.real, h, period, _cache) + \
  102. 1j * tilbert(tmp.imag, h, period, _cache)
  103. if period is not None:
  104. h = h * 2 * pi / period
  105. n = len(x)
  106. omega = _cache.get((n, h))
  107. if omega is None:
  108. if len(_cache) > 20:
  109. while _cache:
  110. _cache.popitem()
  111. def kernel(k, h=h):
  112. if k:
  113. return 1.0/tanh(h*k)
  114. return 0
  115. omega = convolve.init_convolution_kernel(n, kernel, d=1)
  116. _cache[(n,h)] = omega
  117. overwrite_x = _datacopied(tmp, x)
  118. return convolve.convolve(tmp,omega,swap_real_imag=1,overwrite_x=overwrite_x)
  119. def itilbert(x,h,period=None, _cache=_cache):
  120. """
  121. Return inverse h-Tilbert transform of a periodic sequence x.
  122. If ``x_j`` and ``y_j`` are Fourier coefficients of periodic functions x
  123. and y, respectively, then::
  124. y_j = -sqrt(-1)*tanh(j*h*2*pi/period) * x_j
  125. y_0 = 0
  126. For more details, see `tilbert`.
  127. """
  128. if isinstance(_cache, threading.local):
  129. if not hasattr(_cache, 'itilbert_cache'):
  130. _cache.itilbert_cache = {}
  131. _cache = _cache.itilbert_cache
  132. tmp = asarray(x)
  133. if iscomplexobj(tmp):
  134. return itilbert(tmp.real, h, period, _cache) + \
  135. 1j*itilbert(tmp.imag, h, period, _cache)
  136. if period is not None:
  137. h = h*2*pi/period
  138. n = len(x)
  139. omega = _cache.get((n,h))
  140. if omega is None:
  141. if len(_cache) > 20:
  142. while _cache:
  143. _cache.popitem()
  144. def kernel(k,h=h):
  145. if k:
  146. return -tanh(h*k)
  147. return 0
  148. omega = convolve.init_convolution_kernel(n,kernel,d=1)
  149. _cache[(n,h)] = omega
  150. overwrite_x = _datacopied(tmp, x)
  151. return convolve.convolve(tmp,omega,swap_real_imag=1,overwrite_x=overwrite_x)
  152. def hilbert(x, _cache=_cache):
  153. """
  154. Return Hilbert transform of a periodic sequence x.
  155. If x_j and y_j are Fourier coefficients of periodic functions x
  156. and y, respectively, then::
  157. y_j = sqrt(-1)*sign(j) * x_j
  158. y_0 = 0
  159. Parameters
  160. ----------
  161. x : array_like
  162. The input array, should be periodic.
  163. _cache : dict, optional
  164. Dictionary that contains the kernel used to do a convolution with.
  165. Returns
  166. -------
  167. y : ndarray
  168. The transformed input.
  169. See Also
  170. --------
  171. scipy.signal.hilbert : Compute the analytic signal, using the Hilbert
  172. transform.
  173. Notes
  174. -----
  175. If ``sum(x, axis=0) == 0`` then ``hilbert(ihilbert(x)) == x``.
  176. For even len(x), the Nyquist mode of x is taken zero.
  177. The sign of the returned transform does not have a factor -1 that is more
  178. often than not found in the definition of the Hilbert transform. Note also
  179. that `scipy.signal.hilbert` does have an extra -1 factor compared to this
  180. function.
  181. """
  182. if isinstance(_cache, threading.local):
  183. if not hasattr(_cache, 'hilbert_cache'):
  184. _cache.hilbert_cache = {}
  185. _cache = _cache.hilbert_cache
  186. tmp = asarray(x)
  187. if iscomplexobj(tmp):
  188. return hilbert(tmp.real, _cache) + 1j * hilbert(tmp.imag, _cache)
  189. n = len(x)
  190. omega = _cache.get(n)
  191. if omega is None:
  192. if len(_cache) > 20:
  193. while _cache:
  194. _cache.popitem()
  195. def kernel(k):
  196. if k > 0:
  197. return 1.0
  198. elif k < 0:
  199. return -1.0
  200. return 0.0
  201. omega = convolve.init_convolution_kernel(n,kernel,d=1)
  202. _cache[n] = omega
  203. overwrite_x = _datacopied(tmp, x)
  204. return convolve.convolve(tmp,omega,swap_real_imag=1,overwrite_x=overwrite_x)
  205. def ihilbert(x, _cache=_cache):
  206. """
  207. Return inverse Hilbert transform of a periodic sequence x.
  208. If ``x_j`` and ``y_j`` are Fourier coefficients of periodic functions x
  209. and y, respectively, then::
  210. y_j = -sqrt(-1)*sign(j) * x_j
  211. y_0 = 0
  212. """
  213. if isinstance(_cache, threading.local):
  214. if not hasattr(_cache, 'ihilbert_cache'):
  215. _cache.ihilbert_cache = {}
  216. _cache = _cache.ihilbert_cache
  217. return -hilbert(x, _cache)
  218. def cs_diff(x, a, b, period=None, _cache=_cache):
  219. """
  220. Return (a,b)-cosh/sinh pseudo-derivative of a periodic sequence.
  221. If ``x_j`` and ``y_j`` are Fourier coefficients of periodic functions x
  222. and y, respectively, then::
  223. y_j = -sqrt(-1)*cosh(j*a*2*pi/period)/sinh(j*b*2*pi/period) * x_j
  224. y_0 = 0
  225. Parameters
  226. ----------
  227. x : array_like
  228. The array to take the pseudo-derivative from.
  229. a, b : float
  230. Defines the parameters of the cosh/sinh pseudo-differential
  231. operator.
  232. period : float, optional
  233. The period of the sequence. Default period is ``2*pi``.
  234. Returns
  235. -------
  236. cs_diff : ndarray
  237. Pseudo-derivative of periodic sequence `x`.
  238. Notes
  239. -----
  240. For even len(`x`), the Nyquist mode of `x` is taken as zero.
  241. """
  242. if isinstance(_cache, threading.local):
  243. if not hasattr(_cache, 'cs_diff_cache'):
  244. _cache.cs_diff_cache = {}
  245. _cache = _cache.cs_diff_cache
  246. tmp = asarray(x)
  247. if iscomplexobj(tmp):
  248. return cs_diff(tmp.real, a, b, period, _cache) + \
  249. 1j*cs_diff(tmp.imag, a, b, period, _cache)
  250. if period is not None:
  251. a = a*2*pi/period
  252. b = b*2*pi/period
  253. n = len(x)
  254. omega = _cache.get((n,a,b))
  255. if omega is None:
  256. if len(_cache) > 20:
  257. while _cache:
  258. _cache.popitem()
  259. def kernel(k,a=a,b=b):
  260. if k:
  261. return -cosh(a*k)/sinh(b*k)
  262. return 0
  263. omega = convolve.init_convolution_kernel(n,kernel,d=1)
  264. _cache[(n,a,b)] = omega
  265. overwrite_x = _datacopied(tmp, x)
  266. return convolve.convolve(tmp,omega,swap_real_imag=1,overwrite_x=overwrite_x)
  267. def sc_diff(x, a, b, period=None, _cache=_cache):
  268. """
  269. Return (a,b)-sinh/cosh pseudo-derivative of a periodic sequence x.
  270. If x_j and y_j are Fourier coefficients of periodic functions x
  271. and y, respectively, then::
  272. y_j = sqrt(-1)*sinh(j*a*2*pi/period)/cosh(j*b*2*pi/period) * x_j
  273. y_0 = 0
  274. Parameters
  275. ----------
  276. x : array_like
  277. Input array.
  278. a,b : float
  279. Defines the parameters of the sinh/cosh pseudo-differential
  280. operator.
  281. period : float, optional
  282. The period of the sequence x. Default is 2*pi.
  283. Notes
  284. -----
  285. ``sc_diff(cs_diff(x,a,b),b,a) == x``
  286. For even ``len(x)``, the Nyquist mode of x is taken as zero.
  287. """
  288. if isinstance(_cache, threading.local):
  289. if not hasattr(_cache, 'sc_diff_cache'):
  290. _cache.sc_diff_cache = {}
  291. _cache = _cache.sc_diff_cache
  292. tmp = asarray(x)
  293. if iscomplexobj(tmp):
  294. return sc_diff(tmp.real, a, b, period, _cache) + \
  295. 1j * sc_diff(tmp.imag, a, b, period, _cache)
  296. if period is not None:
  297. a = a*2*pi/period
  298. b = b*2*pi/period
  299. n = len(x)
  300. omega = _cache.get((n,a,b))
  301. if omega is None:
  302. if len(_cache) > 20:
  303. while _cache:
  304. _cache.popitem()
  305. def kernel(k,a=a,b=b):
  306. if k:
  307. return sinh(a*k)/cosh(b*k)
  308. return 0
  309. omega = convolve.init_convolution_kernel(n,kernel,d=1)
  310. _cache[(n,a,b)] = omega
  311. overwrite_x = _datacopied(tmp, x)
  312. return convolve.convolve(tmp,omega,swap_real_imag=1,overwrite_x=overwrite_x)
  313. def ss_diff(x, a, b, period=None, _cache=_cache):
  314. """
  315. Return (a,b)-sinh/sinh pseudo-derivative of a periodic sequence x.
  316. If x_j and y_j are Fourier coefficients of periodic functions x
  317. and y, respectively, then::
  318. y_j = sinh(j*a*2*pi/period)/sinh(j*b*2*pi/period) * x_j
  319. y_0 = a/b * x_0
  320. Parameters
  321. ----------
  322. x : array_like
  323. The array to take the pseudo-derivative from.
  324. a,b
  325. Defines the parameters of the sinh/sinh pseudo-differential
  326. operator.
  327. period : float, optional
  328. The period of the sequence x. Default is ``2*pi``.
  329. Notes
  330. -----
  331. ``ss_diff(ss_diff(x,a,b),b,a) == x``
  332. """
  333. if isinstance(_cache, threading.local):
  334. if not hasattr(_cache, 'ss_diff_cache'):
  335. _cache.ss_diff_cache = {}
  336. _cache = _cache.ss_diff_cache
  337. tmp = asarray(x)
  338. if iscomplexobj(tmp):
  339. return ss_diff(tmp.real, a, b, period, _cache) + \
  340. 1j*ss_diff(tmp.imag, a, b, period, _cache)
  341. if period is not None:
  342. a = a*2*pi/period
  343. b = b*2*pi/period
  344. n = len(x)
  345. omega = _cache.get((n,a,b))
  346. if omega is None:
  347. if len(_cache) > 20:
  348. while _cache:
  349. _cache.popitem()
  350. def kernel(k,a=a,b=b):
  351. if k:
  352. return sinh(a*k)/sinh(b*k)
  353. return float(a)/b
  354. omega = convolve.init_convolution_kernel(n,kernel)
  355. _cache[(n,a,b)] = omega
  356. overwrite_x = _datacopied(tmp, x)
  357. return convolve.convolve(tmp,omega,overwrite_x=overwrite_x)
  358. def cc_diff(x, a, b, period=None, _cache=_cache):
  359. """
  360. Return (a,b)-cosh/cosh pseudo-derivative of a periodic sequence.
  361. If x_j and y_j are Fourier coefficients of periodic functions x
  362. and y, respectively, then::
  363. y_j = cosh(j*a*2*pi/period)/cosh(j*b*2*pi/period) * x_j
  364. Parameters
  365. ----------
  366. x : array_like
  367. The array to take the pseudo-derivative from.
  368. a,b : float
  369. Defines the parameters of the sinh/sinh pseudo-differential
  370. operator.
  371. period : float, optional
  372. The period of the sequence x. Default is ``2*pi``.
  373. Returns
  374. -------
  375. cc_diff : ndarray
  376. Pseudo-derivative of periodic sequence `x`.
  377. Notes
  378. -----
  379. ``cc_diff(cc_diff(x,a,b),b,a) == x``
  380. """
  381. if isinstance(_cache, threading.local):
  382. if not hasattr(_cache, 'cc_diff_cache'):
  383. _cache.cc_diff_cache = {}
  384. _cache = _cache.cc_diff_cache
  385. tmp = asarray(x)
  386. if iscomplexobj(tmp):
  387. return cc_diff(tmp.real, a, b, period, _cache) + \
  388. 1j * cc_diff(tmp.imag, a, b, period, _cache)
  389. if period is not None:
  390. a = a*2*pi/period
  391. b = b*2*pi/period
  392. n = len(x)
  393. omega = _cache.get((n,a,b))
  394. if omega is None:
  395. if len(_cache) > 20:
  396. while _cache:
  397. _cache.popitem()
  398. def kernel(k,a=a,b=b):
  399. return cosh(a*k)/cosh(b*k)
  400. omega = convolve.init_convolution_kernel(n,kernel)
  401. _cache[(n,a,b)] = omega
  402. overwrite_x = _datacopied(tmp, x)
  403. return convolve.convolve(tmp,omega,overwrite_x=overwrite_x)
  404. def shift(x, a, period=None, _cache=_cache):
  405. """
  406. Shift periodic sequence x by a: y(u) = x(u+a).
  407. If x_j and y_j are Fourier coefficients of periodic functions x
  408. and y, respectively, then::
  409. y_j = exp(j*a*2*pi/period*sqrt(-1)) * x_f
  410. Parameters
  411. ----------
  412. x : array_like
  413. The array to take the pseudo-derivative from.
  414. a : float
  415. Defines the parameters of the sinh/sinh pseudo-differential
  416. period : float, optional
  417. The period of the sequences x and y. Default period is ``2*pi``.
  418. """
  419. if isinstance(_cache, threading.local):
  420. if not hasattr(_cache, 'shift_cache'):
  421. _cache.shift_cache = {}
  422. _cache = _cache.shift_cache
  423. tmp = asarray(x)
  424. if iscomplexobj(tmp):
  425. return shift(tmp.real, a, period, _cache) + 1j * shift(
  426. tmp.imag, a, period, _cache)
  427. if period is not None:
  428. a = a*2*pi/period
  429. n = len(x)
  430. omega = _cache.get((n,a))
  431. if omega is None:
  432. if len(_cache) > 20:
  433. while _cache:
  434. _cache.popitem()
  435. def kernel_real(k,a=a):
  436. return cos(a*k)
  437. def kernel_imag(k,a=a):
  438. return sin(a*k)
  439. omega_real = convolve.init_convolution_kernel(n,kernel_real,d=0,
  440. zero_nyquist=0)
  441. omega_imag = convolve.init_convolution_kernel(n,kernel_imag,d=1,
  442. zero_nyquist=0)
  443. _cache[(n,a)] = omega_real,omega_imag
  444. else:
  445. omega_real,omega_imag = omega
  446. overwrite_x = _datacopied(tmp, x)
  447. return convolve.convolve_z(tmp,omega_real,omega_imag,
  448. overwrite_x=overwrite_x)