pseudo_diffs.py 14 KB

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