inverselaplace.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. # contributed to mpmath by Kristopher L. Kuhlman, February 2017
  2. class InverseLaplaceTransform(object):
  3. r"""
  4. Inverse Laplace transform methods are implemented using this
  5. class, in order to simplify the code and provide a common
  6. infrastructure.
  7. Implement a custom inverse Laplace transform algorithm by
  8. subclassing :class:`InverseLaplaceTransform` and implementing the
  9. appropriate methods. The subclass can then be used by
  10. :func:`~mpmath.invertlaplace` by passing it as the *method*
  11. argument.
  12. """
  13. def __init__(self,ctx):
  14. self.ctx = ctx
  15. def calc_laplace_parameter(self,t,**kwargs):
  16. r"""
  17. Determine the vector of Laplace parameter values needed for an
  18. algorithm, this will depend on the choice of algorithm (de
  19. Hoog is default), the algorithm-specific parameters passed (or
  20. default ones), and desired time.
  21. """
  22. raise NotImplementedError
  23. def calc_time_domain_solution(self,fp):
  24. r"""
  25. Compute the time domain solution, after computing the
  26. Laplace-space function evaluations at the abscissa required
  27. for the algorithm. Abscissa computed for one algorithm are
  28. typically not useful for another algorithm.
  29. """
  30. raise NotImplementedError
  31. class FixedTalbot(InverseLaplaceTransform):
  32. def calc_laplace_parameter(self,t,**kwargs):
  33. r"""The "fixed" Talbot method deforms the Bromwich contour towards
  34. `-\infty` in the shape of a parabola. Traditionally the Talbot
  35. algorithm has adjustable parameters, but the "fixed" version
  36. does not. The `r` parameter could be passed in as a parameter,
  37. if you want to override the default given by (Abate & Valko,
  38. 2004).
  39. The Laplace parameter is sampled along a parabola opening
  40. along the negative imaginary axis, with the base of the
  41. parabola along the real axis at
  42. `p=\frac{r}{t_\mathrm{max}}`. As the number of terms used in
  43. the approximation (degree) grows, the abscissa required for
  44. function evaluation tend towards `-\infty`, requiring high
  45. precision to prevent overflow. If any poles, branch cuts or
  46. other singularities exist such that the deformed Bromwich
  47. contour lies to the left of the singularity, the method will
  48. fail.
  49. **Optional arguments**
  50. :class:`~mpmath.calculus.inverselaplace.FixedTalbot.calc_laplace_parameter`
  51. recognizes the following keywords
  52. *tmax*
  53. maximum time associated with vector of times
  54. (typically just the time requested)
  55. *degree*
  56. integer order of approximation (M = number of terms)
  57. *r*
  58. abscissa for `p_0` (otherwise computed using rule
  59. of thumb `2M/5`)
  60. The working precision will be increased according to a rule of
  61. thumb. If 'degree' is not specified, the working precision and
  62. degree are chosen to hopefully achieve the dps of the calling
  63. context. If 'degree' is specified, the working precision is
  64. chosen to achieve maximum resulting precision for the
  65. specified degree.
  66. .. math ::
  67. p_0=\frac{r}{t}
  68. .. math ::
  69. p_i=\frac{i r \pi}{Mt_\mathrm{max}}\left[\cot\left(
  70. \frac{i\pi}{M}\right) + j \right] \qquad 1\le i <M
  71. where `j=\sqrt{-1}`, `r=2M/5`, and `t_\mathrm{max}` is the
  72. maximum specified time.
  73. """
  74. # required
  75. # ------------------------------
  76. # time of desired approximation
  77. self.t = self.ctx.convert(t)
  78. # optional
  79. # ------------------------------
  80. # maximum time desired (used for scaling) default is requested
  81. # time.
  82. self.tmax = self.ctx.convert(kwargs.get('tmax',self.t))
  83. # empirical relationships used here based on a linear fit of
  84. # requested and delivered dps for exponentially decaying time
  85. # functions for requested dps up to 512.
  86. if 'degree' in kwargs:
  87. self.degree = kwargs['degree']
  88. self.dps_goal = self.degree
  89. else:
  90. self.dps_goal = int(1.72*self.ctx.dps)
  91. self.degree = max(12,int(1.38*self.dps_goal))
  92. M = self.degree
  93. # this is adjusting the dps of the calling context hopefully
  94. # the caller doesn't monkey around with it between calling
  95. # this routine and calc_time_domain_solution()
  96. self.dps_orig = self.ctx.dps
  97. self.ctx.dps = self.dps_goal
  98. # Abate & Valko rule of thumb for r parameter
  99. self.r = kwargs.get('r',self.ctx.fraction(2,5)*M)
  100. self.theta = self.ctx.linspace(0.0, self.ctx.pi, M+1)
  101. self.cot_theta = self.ctx.matrix(M,1)
  102. self.cot_theta[0] = 0 # not used
  103. # all but time-dependent part of p
  104. self.delta = self.ctx.matrix(M,1)
  105. self.delta[0] = self.r
  106. for i in range(1,M):
  107. self.cot_theta[i] = self.ctx.cot(self.theta[i])
  108. self.delta[i] = self.r*self.theta[i]*(self.cot_theta[i] + 1j)
  109. self.p = self.ctx.matrix(M,1)
  110. self.p = self.delta/self.tmax
  111. # NB: p is complex (mpc)
  112. def calc_time_domain_solution(self,fp,t,manual_prec=False):
  113. r"""The fixed Talbot time-domain solution is computed from the
  114. Laplace-space function evaluations using
  115. .. math ::
  116. f(t,M)=\frac{2}{5t}\sum_{k=0}^{M-1}\Re \left[
  117. \gamma_k \bar{f}(p_k)\right]
  118. where
  119. .. math ::
  120. \gamma_0 = \frac{1}{2}e^{r}\bar{f}(p_0)
  121. .. math ::
  122. \gamma_k = e^{tp_k}\left\lbrace 1 + \frac{jk\pi}{M}\left[1 +
  123. \cot \left( \frac{k \pi}{M} \right)^2 \right] - j\cot\left(
  124. \frac{k \pi}{M}\right)\right \rbrace \qquad 1\le k<M.
  125. Again, `j=\sqrt{-1}`.
  126. Before calling this function, call
  127. :class:`~mpmath.calculus.inverselaplace.FixedTalbot.calc_laplace_parameter`
  128. to set the parameters and compute the required coefficients.
  129. **References**
  130. 1. Abate, J., P. Valko (2004). Multi-precision Laplace
  131. transform inversion. *International Journal for Numerical
  132. Methods in Engineering* 60:979-993,
  133. http://dx.doi.org/10.1002/nme.995
  134. 2. Talbot, A. (1979). The accurate numerical inversion of
  135. Laplace transforms. *IMA Journal of Applied Mathematics*
  136. 23(1):97, http://dx.doi.org/10.1093/imamat/23.1.97
  137. """
  138. # required
  139. # ------------------------------
  140. self.t = self.ctx.convert(t)
  141. # assume fp was computed from p matrix returned from
  142. # calc_laplace_parameter(), so is already a list or matrix of
  143. # mpmath 'mpc' types
  144. # these were computed in previous call to
  145. # calc_laplace_parameter()
  146. theta = self.theta
  147. delta = self.delta
  148. M = self.degree
  149. p = self.p
  150. r = self.r
  151. ans = self.ctx.matrix(M,1)
  152. ans[0] = self.ctx.exp(delta[0])*fp[0]/2
  153. for i in range(1,M):
  154. ans[i] = self.ctx.exp(delta[i])*fp[i]*(
  155. 1 + 1j*theta[i]*(1 + self.cot_theta[i]**2) -
  156. 1j*self.cot_theta[i])
  157. result = self.ctx.fraction(2,5)*self.ctx.fsum(ans)/self.t
  158. # setting dps back to value when calc_laplace_parameter was
  159. # called, unless flag is set.
  160. if not manual_prec:
  161. self.ctx.dps = self.dps_orig
  162. return result.real
  163. # ****************************************
  164. class Stehfest(InverseLaplaceTransform):
  165. def calc_laplace_parameter(self,t,**kwargs):
  166. r"""
  167. The Gaver-Stehfest method is a discrete approximation of the
  168. Widder-Post inversion algorithm, rather than a direct
  169. approximation of the Bromwich contour integral.
  170. The method abscissa along the real axis, and therefore has
  171. issues inverting oscillatory functions (which have poles in
  172. pairs away from the real axis).
  173. The working precision will be increased according to a rule of
  174. thumb. If 'degree' is not specified, the working precision and
  175. degree are chosen to hopefully achieve the dps of the calling
  176. context. If 'degree' is specified, the working precision is
  177. chosen to achieve maximum resulting precision for the
  178. specified degree.
  179. .. math ::
  180. p_k = \frac{k \log 2}{t} \qquad 1 \le k \le M
  181. """
  182. # required
  183. # ------------------------------
  184. # time of desired approximation
  185. self.t = self.ctx.convert(t)
  186. # optional
  187. # ------------------------------
  188. # empirical relationships used here based on a linear fit of
  189. # requested and delivered dps for exponentially decaying time
  190. # functions for requested dps up to 512.
  191. if 'degree' in kwargs:
  192. self.degree = kwargs['degree']
  193. self.dps_goal = int(1.38*self.degree)
  194. else:
  195. self.dps_goal = int(2.93*self.ctx.dps)
  196. self.degree = max(16,self.dps_goal)
  197. # _coeff routine requires even degree
  198. if self.degree%2 > 0:
  199. self.degree += 1
  200. M = self.degree
  201. # this is adjusting the dps of the calling context
  202. # hopefully the caller doesn't monkey around with it
  203. # between calling this routine and calc_time_domain_solution()
  204. self.dps_orig = self.ctx.dps
  205. self.ctx.dps = self.dps_goal
  206. self.V = self._coeff()
  207. self.p = self.ctx.matrix(self.ctx.arange(1,M+1))*self.ctx.ln2/self.t
  208. # NB: p is real (mpf)
  209. def _coeff(self):
  210. r"""Salzer summation weights (aka, "Stehfest coefficients")
  211. only depend on the approximation order (M) and the precision"""
  212. M = self.degree
  213. M2 = int(M/2) # checked earlier that M is even
  214. V = self.ctx.matrix(M,1)
  215. # Salzer summation weights
  216. # get very large in magnitude and oscillate in sign,
  217. # if the precision is not high enough, there will be
  218. # catastrophic cancellation
  219. for k in range(1,M+1):
  220. z = self.ctx.matrix(min(k,M2)+1,1)
  221. for j in range(int((k+1)/2),min(k,M2)+1):
  222. z[j] = (self.ctx.power(j,M2)*self.ctx.fac(2*j)/
  223. (self.ctx.fac(M2-j)*self.ctx.fac(j)*
  224. self.ctx.fac(j-1)*self.ctx.fac(k-j)*
  225. self.ctx.fac(2*j-k)))
  226. V[k-1] = self.ctx.power(-1,k+M2)*self.ctx.fsum(z)
  227. return V
  228. def calc_time_domain_solution(self,fp,t,manual_prec=False):
  229. r"""Compute time-domain Stehfest algorithm solution.
  230. .. math ::
  231. f(t,M) = \frac{\log 2}{t} \sum_{k=1}^{M} V_k \bar{f}\left(
  232. p_k \right)
  233. where
  234. .. math ::
  235. V_k = (-1)^{k + N/2} \sum^{\min(k,N/2)}_{i=\lfloor(k+1)/2 \rfloor}
  236. \frac{i^{\frac{N}{2}}(2i)!}{\left(\frac{N}{2}-i \right)! \, i! \,
  237. \left(i-1 \right)! \, \left(k-i\right)! \, \left(2i-k \right)!}
  238. As the degree increases, the abscissa (`p_k`) only increase
  239. linearly towards `\infty`, but the Stehfest coefficients
  240. (`V_k`) alternate in sign and increase rapidly in sign,
  241. requiring high precision to prevent overflow or loss of
  242. significance when evaluating the sum.
  243. **References**
  244. 1. Widder, D. (1941). *The Laplace Transform*. Princeton.
  245. 2. Stehfest, H. (1970). Algorithm 368: numerical inversion of
  246. Laplace transforms. *Communications of the ACM* 13(1):47-49,
  247. http://dx.doi.org/10.1145/361953.361969
  248. """
  249. # required
  250. self.t = self.ctx.convert(t)
  251. # assume fp was computed from p matrix returned from
  252. # calc_laplace_parameter(), so is already
  253. # a list or matrix of mpmath 'mpf' types
  254. result = self.ctx.fdot(self.V,fp)*self.ctx.ln2/self.t
  255. # setting dps back to value when calc_laplace_parameter was called
  256. if not manual_prec:
  257. self.ctx.dps = self.dps_orig
  258. # ignore any small imaginary part
  259. return result.real
  260. # ****************************************
  261. class deHoog(InverseLaplaceTransform):
  262. def calc_laplace_parameter(self,t,**kwargs):
  263. r"""the de Hoog, Knight & Stokes algorithm is an
  264. accelerated form of the Fourier series numerical
  265. inverse Laplace transform algorithms.
  266. .. math ::
  267. p_k = \gamma + \frac{jk}{T} \qquad 0 \le k < 2M+1
  268. where
  269. .. math ::
  270. \gamma = \alpha - \frac{\log \mathrm{tol}}{2T},
  271. `j=\sqrt{-1}`, `T = 2t_\mathrm{max}` is a scaled time,
  272. `\alpha=10^{-\mathrm{dps\_goal}}` is the real part of the
  273. rightmost pole or singularity, which is chosen based on the
  274. desired accuracy (assuming the rightmost singularity is 0),
  275. and `\mathrm{tol}=10\alpha` is the desired tolerance, which is
  276. chosen in relation to `\alpha`.`
  277. When increasing the degree, the abscissa increase towards
  278. `j\infty`, but more slowly than the fixed Talbot
  279. algorithm. The de Hoog et al. algorithm typically does better
  280. with oscillatory functions of time, and less well-behaved
  281. functions. The method tends to be slower than the Talbot and
  282. Stehfest algorithsm, especially so at very high precision
  283. (e.g., `>500` digits precision).
  284. """
  285. # required
  286. # ------------------------------
  287. self.t = self.ctx.convert(t)
  288. # optional
  289. # ------------------------------
  290. self.tmax = kwargs.get('tmax',self.t)
  291. # empirical relationships used here based on a linear fit of
  292. # requested and delivered dps for exponentially decaying time
  293. # functions for requested dps up to 512.
  294. if 'degree' in kwargs:
  295. self.degree = kwargs['degree']
  296. self.dps_goal = int(1.38*self.degree)
  297. else:
  298. self.dps_goal = int(self.ctx.dps*1.36)
  299. self.degree = max(10,self.dps_goal)
  300. # 2*M+1 terms in approximation
  301. M = self.degree
  302. # adjust alpha component of abscissa of convergence for higher
  303. # precision
  304. tmp = self.ctx.power(10.0,-self.dps_goal)
  305. self.alpha = self.ctx.convert(kwargs.get('alpha',tmp))
  306. # desired tolerance (here simply related to alpha)
  307. self.tol = self.ctx.convert(kwargs.get('tol',self.alpha*10.0))
  308. self.np = 2*self.degree+1 # number of terms in approximation
  309. # this is adjusting the dps of the calling context
  310. # hopefully the caller doesn't monkey around with it
  311. # between calling this routine and calc_time_domain_solution()
  312. self.dps_orig = self.ctx.dps
  313. self.ctx.dps = self.dps_goal
  314. # scaling factor (likely tun-able, but 2 is typical)
  315. self.scale = kwargs.get('scale',2)
  316. self.T = self.ctx.convert(kwargs.get('T',self.scale*self.tmax))
  317. self.p = self.ctx.matrix(2*M+1,1)
  318. self.gamma = self.alpha - self.ctx.log(self.tol)/(self.scale*self.T)
  319. self.p = (self.gamma + self.ctx.pi*
  320. self.ctx.matrix(self.ctx.arange(self.np))/self.T*1j)
  321. # NB: p is complex (mpc)
  322. def calc_time_domain_solution(self,fp,t,manual_prec=False):
  323. r"""Calculate time-domain solution for
  324. de Hoog, Knight & Stokes algorithm.
  325. The un-accelerated Fourier series approach is:
  326. .. math ::
  327. f(t,2M+1) = \frac{e^{\gamma t}}{T} \sum_{k=0}^{2M}{}^{'}
  328. \Re\left[\bar{f}\left( p_k \right)
  329. e^{i\pi t/T} \right],
  330. where the prime on the summation indicates the first term is halved.
  331. This simplistic approach requires so many function evaluations
  332. that it is not practical. Non-linear acceleration is
  333. accomplished via Pade-approximation and an analytic expression
  334. for the remainder of the continued fraction. See the original
  335. paper (reference 2 below) a detailed description of the
  336. numerical approach.
  337. **References**
  338. 1. Davies, B. (2005). *Integral Transforms and their
  339. Applications*, Third Edition. Springer.
  340. 2. de Hoog, F., J. Knight, A. Stokes (1982). An improved
  341. method for numerical inversion of Laplace transforms. *SIAM
  342. Journal of Scientific and Statistical Computing* 3:357-366,
  343. http://dx.doi.org/10.1137/0903022
  344. """
  345. M = self.degree
  346. np = self.np
  347. T = self.T
  348. self.t = self.ctx.convert(t)
  349. # would it be useful to try re-using
  350. # space between e&q and A&B?
  351. e = self.ctx.zeros(np,M+1)
  352. q = self.ctx.matrix(2*M,M)
  353. d = self.ctx.matrix(np,1)
  354. A = self.ctx.zeros(np+1,1)
  355. B = self.ctx.ones(np+1,1)
  356. # initialize Q-D table
  357. e[:,0] = 0.0 + 0j
  358. q[0,0] = fp[1]/(fp[0]/2)
  359. for i in range(1,2*M):
  360. q[i,0] = fp[i+1]/fp[i]
  361. # rhombus rule for filling triangular Q-D table (e & q)
  362. for r in range(1,M+1):
  363. # start with e, column 1, 0:2*M-2
  364. mr = 2*(M-r) + 1
  365. e[0:mr,r] = q[1:mr+1,r-1] - q[0:mr,r-1] + e[1:mr+1,r-1]
  366. if not r == M:
  367. rq = r+1
  368. mr = 2*(M-rq)+1 + 2
  369. for i in range(mr):
  370. q[i,rq-1] = q[i+1,rq-2]*e[i+1,rq-1]/e[i,rq-1]
  371. # build up continued fraction coefficients (d)
  372. d[0] = fp[0]/2
  373. for r in range(1,M+1):
  374. d[2*r-1] = -q[0,r-1] # even terms
  375. d[2*r] = -e[0,r] # odd terms
  376. # seed A and B for recurrence
  377. A[0] = 0.0 + 0.0j
  378. A[1] = d[0]
  379. B[0:2] = 1.0 + 0.0j
  380. # base of the power series
  381. z = self.ctx.expjpi(self.t/T) # i*pi is already in fcn
  382. # coefficients of Pade approximation (A & B)
  383. # using recurrence for all but last term
  384. for i in range(1,2*M):
  385. A[i+1] = A[i] + d[i]*A[i-1]*z
  386. B[i+1] = B[i] + d[i]*B[i-1]*z
  387. # "improved remainder" to continued fraction
  388. brem = (1 + (d[2*M-1] - d[2*M])*z)/2
  389. # powm1(x,y) computes x^y - 1 more accurately near zero
  390. rem = brem*self.ctx.powm1(1 + d[2*M]*z/brem,
  391. self.ctx.fraction(1,2))
  392. # last term of recurrence using new remainder
  393. A[np] = A[2*M] + rem*A[2*M-1]
  394. B[np] = B[2*M] + rem*B[2*M-1]
  395. # diagonal Pade approximation
  396. # F=A/B represents accelerated trapezoid rule
  397. result = self.ctx.exp(self.gamma*self.t)/T*(A[np]/B[np]).real
  398. # setting dps back to value when calc_laplace_parameter was called
  399. if not manual_prec:
  400. self.ctx.dps = self.dps_orig
  401. return result
  402. # ****************************************
  403. class LaplaceTransformInversionMethods(object):
  404. def __init__(ctx, *args, **kwargs):
  405. ctx._fixed_talbot = FixedTalbot(ctx)
  406. ctx._stehfest = Stehfest(ctx)
  407. ctx._de_hoog = deHoog(ctx)
  408. def invertlaplace(ctx, f, t, **kwargs):
  409. r"""Computes the numerical inverse Laplace transform for a
  410. Laplace-space function at a given time. The function being
  411. evaluated is assumed to be a real-valued function of time.
  412. The user must supply a Laplace-space function `\bar{f}(p)`,
  413. and a desired time at which to estimate the time-domain
  414. solution `f(t)`.
  415. A few basic examples of Laplace-space functions with known
  416. inverses (see references [1,2]) :
  417. .. math ::
  418. \mathcal{L}\left\lbrace f(t) \right\rbrace=\bar{f}(p)
  419. .. math ::
  420. \mathcal{L}^{-1}\left\lbrace \bar{f}(p) \right\rbrace = f(t)
  421. .. math ::
  422. \bar{f}(p) = \frac{1}{(p+1)^2}
  423. .. math ::
  424. f(t) = t e^{-t}
  425. >>> from mpmath import *
  426. >>> mp.dps = 15; mp.pretty = True
  427. >>> tt = [0.001, 0.01, 0.1, 1, 10]
  428. >>> fp = lambda p: 1/(p+1)**2
  429. >>> ft = lambda t: t*exp(-t)
  430. >>> ft(tt[0]),ft(tt[0])-invertlaplace(fp,tt[0],method='talbot')
  431. (0.000999000499833375, 8.57923043561212e-20)
  432. >>> ft(tt[1]),ft(tt[1])-invertlaplace(fp,tt[1],method='talbot')
  433. (0.00990049833749168, 3.27007646698047e-19)
  434. >>> ft(tt[2]),ft(tt[2])-invertlaplace(fp,tt[2],method='talbot')
  435. (0.090483741803596, -1.75215800052168e-18)
  436. >>> ft(tt[3]),ft(tt[3])-invertlaplace(fp,tt[3],method='talbot')
  437. (0.367879441171442, 1.2428864009344e-17)
  438. >>> ft(tt[4]),ft(tt[4])-invertlaplace(fp,tt[4],method='talbot')
  439. (0.000453999297624849, 4.04513489306658e-20)
  440. The methods also work for higher precision:
  441. >>> mp.dps = 100; mp.pretty = True
  442. >>> nstr(ft(tt[0]),15),nstr(ft(tt[0])-invertlaplace(fp,tt[0],method='talbot'),15)
  443. ('0.000999000499833375', '-4.96868310693356e-105')
  444. >>> nstr(ft(tt[1]),15),nstr(ft(tt[1])-invertlaplace(fp,tt[1],method='talbot'),15)
  445. ('0.00990049833749168', '1.23032291513122e-104')
  446. .. math ::
  447. \bar{f}(p) = \frac{1}{p^2+1}
  448. .. math ::
  449. f(t) = \mathrm{J}_0(t)
  450. >>> mp.dps = 15; mp.pretty = True
  451. >>> fp = lambda p: 1/sqrt(p*p + 1)
  452. >>> ft = lambda t: besselj(0,t)
  453. >>> ft(tt[0]),ft(tt[0])-invertlaplace(fp,tt[0])
  454. (0.999999750000016, -6.09717765032273e-18)
  455. >>> ft(tt[1]),ft(tt[1])-invertlaplace(fp,tt[1])
  456. (0.99997500015625, -5.61756281076169e-17)
  457. .. math ::
  458. \bar{f}(p) = \frac{\log p}{p}
  459. .. math ::
  460. f(t) = -\gamma -\log t
  461. >>> mp.dps = 15; mp.pretty = True
  462. >>> fp = lambda p: log(p)/p
  463. >>> ft = lambda t: -euler-log(t)
  464. >>> ft(tt[0]),ft(tt[0])-invertlaplace(fp,tt[0],method='stehfest')
  465. (6.3305396140806, -1.92126634837863e-16)
  466. >>> ft(tt[1]),ft(tt[1])-invertlaplace(fp,tt[1],method='stehfest')
  467. (4.02795452108656, -4.81486093200704e-16)
  468. **Options**
  469. :func:`~mpmath.invertlaplace` recognizes the following optional keywords
  470. valid for all methods:
  471. *method*
  472. Chooses numerical inverse Laplace transform algorithm
  473. (described below).
  474. *degree*
  475. Number of terms used in the approximation
  476. **Algorithms**
  477. Mpmath implements three numerical inverse Laplace transform
  478. algorithms, attributed to: Talbot, Stehfest, and de Hoog,
  479. Knight and Stokes. These can be selected by using
  480. *method='talbot'*, *method='stehfest'*, or *method='dehoog'*
  481. or by passing the classes *method=FixedTalbot*,
  482. *method=Stehfest*, or *method=deHoog*. The functions
  483. :func:`~mpmath.invlaptalbot`, :func:`~mpmath.invlapstehfest`,
  484. and :func:`~mpmath.invlapdehoog` are also available as
  485. shortcuts.
  486. All three algorithms implement a heuristic balance between the
  487. requested precision and the precision used internally for the
  488. calculations. This has been tuned for a typical exponentially
  489. decaying function and precision up to few hundred decimal
  490. digits.
  491. The Laplace transform converts the variable time (i.e., along
  492. a line) into a parameter given by the right half of the
  493. complex `p`-plane. Singularities, poles, and branch cuts in
  494. the complex `p`-plane contain all the information regarding
  495. the time behavior of the corresponding function. Any numerical
  496. method must therefore sample `p`-plane "close enough" to the
  497. singularities to accurately characterize them, while not
  498. getting too close to have catastrophic cancellation, overflow,
  499. or underflow issues. Most significantly, if one or more of the
  500. singularities in the `p`-plane is not on the left side of the
  501. Bromwich contour, its effects will be left out of the computed
  502. solution, and the answer will be completely wrong.
  503. *Talbot*
  504. The fixed Talbot method is high accuracy and fast, but the
  505. method can catastrophically fail for certain classes of time-domain
  506. behavior, including a Heaviside step function for positive
  507. time (e.g., `H(t-2)`), or some oscillatory behaviors. The
  508. Talbot method usually has adjustable parameters, but the
  509. "fixed" variety implemented here does not. This method
  510. deforms the Bromwich integral contour in the shape of a
  511. parabola towards `-\infty`, which leads to problems
  512. when the solution has a decaying exponential in it (e.g., a
  513. Heaviside step function is equivalent to multiplying by a
  514. decaying exponential in Laplace space).
  515. *Stehfest*
  516. The Stehfest algorithm only uses abscissa along the real axis
  517. of the complex `p`-plane to estimate the time-domain
  518. function. Oscillatory time-domain functions have poles away
  519. from the real axis, so this method does not work well with
  520. oscillatory functions, especially high-frequency ones. This
  521. method also depends on summation of terms in a series that
  522. grows very large, and will have catastrophic cancellation
  523. during summation if the working precision is too low.
  524. *de Hoog et al.*
  525. The de Hoog, Knight, and Stokes method is essentially a
  526. Fourier-series quadrature-type approximation to the Bromwich
  527. contour integral, with non-linear series acceleration and an
  528. analytical expression for the remainder term. This method is
  529. typically the most robust and is therefore the default
  530. method. This method also involves the greatest amount of
  531. overhead, so it is typically the slowest of the three methods
  532. at high precision.
  533. **Singularities**
  534. All numerical inverse Laplace transform methods have problems
  535. at large time when the Laplace-space function has poles,
  536. singularities, or branch cuts to the right of the origin in
  537. the complex plane. For simple poles in `\bar{f}(p)` at the
  538. `p`-plane origin, the time function is constant in time (e.g.,
  539. `\mathcal{L}\left\lbrace 1 \right\rbrace=1/p` has a pole at
  540. `p=0`). A pole in `\bar{f}(p)` to the left of the origin is a
  541. decreasing function of time (e.g., `\mathcal{L}\left\lbrace
  542. e^{-t/2} \right\rbrace=1/(p+1/2)` has a pole at `p=-1/2`), and
  543. a pole to the right of the origin leads to an increasing
  544. function in time (e.g., `\mathcal{L}\left\lbrace t e^{t/4}
  545. \right\rbrace = 1/(p-1/4)^2` has a pole at `p=1/4`). When
  546. singularities occur off the real `p` axis, the time-domain
  547. function is oscillatory. For example `\mathcal{L}\left\lbrace
  548. \mathrm{J}_0(t) \right\rbrace=1/\sqrt{p^2+1}` has a branch cut
  549. starting at `p=j=\sqrt{-1}` and is a decaying oscillatory
  550. function, This range of behaviors is illustrated in Duffy [3]
  551. Figure 4.10.4, p. 228.
  552. In general as `p \rightarrow \infty` `t \rightarrow 0` and
  553. vice-versa. All numerical inverse Laplace transform methods
  554. require their abscissa to shift closer to the origin for
  555. larger times. If the abscissa shift left of the rightmost
  556. singularity in the Laplace domain, the answer will be
  557. completely wrong (the effect of singularities to the right of
  558. the Bromwich contour are not included in the results).
  559. For example, the following exponentially growing function has
  560. a pole at `p=3`:
  561. .. math ::
  562. \bar{f}(p)=\frac{1}{p^2-9}
  563. .. math ::
  564. f(t)=\frac{1}{3}\sinh 3t
  565. >>> mp.dps = 15; mp.pretty = True
  566. >>> fp = lambda p: 1/(p*p-9)
  567. >>> ft = lambda t: sinh(3*t)/3
  568. >>> tt = [0.01,0.1,1.0,10.0]
  569. >>> ft(tt[0]),invertlaplace(fp,tt[0],method='talbot')
  570. (0.0100015000675014, 0.0100015000675014)
  571. >>> ft(tt[1]),invertlaplace(fp,tt[1],method='talbot')
  572. (0.101506764482381, 0.101506764482381)
  573. >>> ft(tt[2]),invertlaplace(fp,tt[2],method='talbot')
  574. (3.33929164246997, 3.33929164246997)
  575. >>> ft(tt[3]),invertlaplace(fp,tt[3],method='talbot')
  576. (1781079096920.74, -1.61331069624091e-14)
  577. **References**
  578. 1. [DLMF]_ section 1.14 (http://dlmf.nist.gov/1.14T4)
  579. 2. Cohen, A.M. (2007). Numerical Methods for Laplace Transform
  580. Inversion, Springer.
  581. 3. Duffy, D.G. (1998). Advanced Engineering Mathematics, CRC Press.
  582. **Numerical Inverse Laplace Transform Reviews**
  583. 1. Bellman, R., R.E. Kalaba, J.A. Lockett (1966). *Numerical
  584. inversion of the Laplace transform: Applications to Biology,
  585. Economics, Engineering, and Physics*. Elsevier.
  586. 2. Davies, B., B. Martin (1979). Numerical inversion of the
  587. Laplace transform: a survey and comparison of methods. *Journal
  588. of Computational Physics* 33:1-32,
  589. http://dx.doi.org/10.1016/0021-9991(79)90025-1
  590. 3. Duffy, D.G. (1993). On the numerical inversion of Laplace
  591. transforms: Comparison of three new methods on characteristic
  592. problems from applications. *ACM Transactions on Mathematical
  593. Software* 19(3):333-359, http://dx.doi.org/10.1145/155743.155788
  594. 4. Kuhlman, K.L., (2013). Review of Inverse Laplace Transform
  595. Algorithms for Laplace-Space Numerical Approaches, *Numerical
  596. Algorithms*, 63(2):339-355.
  597. http://dx.doi.org/10.1007/s11075-012-9625-3
  598. """
  599. rule = kwargs.get('method','dehoog')
  600. if type(rule) is str:
  601. lrule = rule.lower()
  602. if lrule == 'talbot':
  603. rule = ctx._fixed_talbot
  604. elif lrule == 'stehfest':
  605. rule = ctx._stehfest
  606. elif lrule == 'dehoog':
  607. rule = ctx._de_hoog
  608. else:
  609. raise ValueError("unknown invlap algorithm: %s" % rule)
  610. else:
  611. rule = rule(ctx)
  612. # determine the vector of Laplace-space parameter
  613. # needed for the requested method and desired time
  614. rule.calc_laplace_parameter(t,**kwargs)
  615. # compute the Laplace-space function evalutations
  616. # at the required abscissa.
  617. fp = [f(p) for p in rule.p]
  618. # compute the time-domain solution from the
  619. # Laplace-space function evaluations
  620. return rule.calc_time_domain_solution(fp,t)
  621. # shortcuts for the above function for specific methods
  622. def invlaptalbot(ctx, *args, **kwargs):
  623. kwargs['method'] = 'talbot'
  624. return ctx.invertlaplace(*args, **kwargs)
  625. def invlapstehfest(ctx, *args, **kwargs):
  626. kwargs['method'] = 'stehfest'
  627. return ctx.invertlaplace(*args, **kwargs)
  628. def invlapdehoog(ctx, *args, **kwargs):
  629. kwargs['method'] = 'dehoog'
  630. return ctx.invertlaplace(*args, **kwargs)
  631. # ****************************************
  632. if __name__ == '__main__':
  633. import doctest
  634. doctest.testmod()