_pseudo_diffs.py 14 KB

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