NUMcomplex.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /* NUMcomplex.cpp
  2. *
  3. * Copyright (C) 2017 David Weenink
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <cmath>
  19. #include <complex>
  20. #include "NUMcomplex.h"
  21. /*
  22. The code to calculate the complex incomplete gamma function was translated from fortran code into c++ by David Weenink.
  23. The fortran code is from the following article:
  24. Eric Kostlan & Dmitry Gokhman, A program for calculating the incomplete gamma function.
  25. Technical report, Dept. of Mathematics, Univ. of California, Berkeley, 1987.
  26. Their algorithm calcutes the complex incomplete gamma function Γ(α,x) by using the following formula:
  27. (1) Γ(α,x)= exp(-x)x^α / h(α,x),
  28. where h(α,x) is a continued fraction:
  29. (2) h(α,x)=x+(1-α)
  30. -----
  31. 1+1
  32. -------
  33. x+(2-α)
  34. ------
  35. 1+2
  36. ------
  37. x+(3-α)
  38. -------
  39. 1+...
  40. Efficient calculation of h(α,x) is possible because consecutive terms of a continued fraction can be computed by means of
  41. a two term linear recursion relation.
  42. If for the sequences {p[k]} and {q[k]}, k=0,1,2,... the following difference equations hold
  43. (3) y[k+2]=x*y[k+1]+((k+2)/2)* y[k] for k even
  44. =y[k+1]((k+3)/2-α)*y[k] for k is odd
  45. and initial conditions p[0]=x, p[1]=x+1-α, q[0]=1, q[1]=1, then p[k]/q[k] is the k-th term
  46. in the continued fraction for h(α,x).
  47. The value of h(α+1,x) can be reduced to h(α,x)
  48. (4) x/h(α+1,x) = α/h(α,x)+1 by using the following relation between gamma functions:
  49. Γ(α+1,x)=αΓ(α,x)+x^α exp(-x).
  50. If x is near the negative real axis then x can be moved to some new value y:
  51. (5) Γ(α,y)-Γ(α,x)=sum(n=0,Infinity,(-1)^n(x^(α+n)-y^(α+n))/(n!(α+n)),
  52. where in the code below a valye of y=1 is used to facilitate the computation.
  53. Numerical considerations:
  54. 1. In formula (5) Kostlan & Gokhman use y=1 to facilitate the computation
  55. 2. If (α,x) is near (−n ,0), where n is a non-negative integer, but x is not equal to zero,
  56. the n-th term in (5) can turn out to be 0.0/0.0. They replace this term with -log(x)-(α+n)log(x)²/2.
  57. Essentially they are helping the computer to calculate (1-x^(α+n))/(α+n).
  58. Problems:
  59. Kostlan & Gokhman notice three intrinsic problems that any algorithm for calculating Γ(α,x) must face:
  60. 1. For x=0 and α a nonpositive integer Γ(α,x)=∞.
  61. 2. If α is not an integer Γ(α,x) is a multi-valued function with a branch point at x=0
  62. 3. As we approach ∞ along certain directions computation of Γ(α,x) is limited because of the inability to compute x^α accurately.
  63. */
  64. static double norm1 (std::complex<double> *x) {
  65. return fabs (real (*x)) + fabs (imag (*x));
  66. }
  67. static void xShiftTerm (std::complex<double> *alpha, std::complex<double> *x, integer i, std::complex<double> *p, std::complex<double> *q) {
  68. // Calculate p*q = (-1)^i (1-x^(alpha+i))/(alpha+i)i!
  69. std::complex<double> zero = 0.0;
  70. double tol = 3e-7, xlim = 39.0, di = i;
  71. if (i == 0) {
  72. *q = 1.0;
  73. }
  74. std::complex<double> alphai = *alpha + di;
  75. if (*x == zero) {
  76. *p = 1.0 / alphai;
  77. if (i != 0) {
  78. *q /= -di;
  79. }
  80. return;
  81. }
  82. std::complex<double> cdlx = log (*x);
  83. // If (1-x**alphai) = -x**alphai,
  84. // then change the inductive scheme to avoid overflow.
  85. if (real (alphai * cdlx) > xlim && i != 0) {
  86. *p *= (alphai - 1.0) / alphai;
  87. *q *= - *x / di;
  88. return;
  89. }
  90. if (norm1 (& alphai) > tol) {
  91. *p = (1.0 - std::pow (*x, alphai)) / alphai;
  92. } else {
  93. *p = -cdlx * (1.0 + cdlx * alphai * 0.5);
  94. }
  95. if (i == 0) {
  96. *q /= - di;
  97. }
  98. }
  99. static void continuedFractionExpansion (std::complex<double> *alpha, std::complex<double> *x, std::complex<double> *result) {
  100. std::complex<double> zero (0.0,0.0);
  101. std::complex<double> q0 = 1.0, q1 = 1.0, p0 = *x, p1 = *x + 1.0 - *alpha, r0;
  102. double tol1 = 1e10, tol2 = 1e-10, error = 1e-18;
  103. for (integer i = 1; i <= 100000; i++) {
  104. double di = i;
  105. if (p0 != zero && q0 != zero && q1 != zero) {
  106. r0 = p0 / q0;
  107. *result = p1 / q1;
  108. std::complex<double> r0mr1 = r0 - *result;
  109. if (norm1 (& r0mr1) < norm1 (result) * error) {
  110. return;
  111. }
  112. // renormalize to avoid underflow or overflow
  113. if (norm1 (& p0) > tol1 || norm1 (& p0) < tol2 || norm1 (& q0) > tol1 || norm1 (& q0) < tol2) {
  114. std::complex<double> factor = p0 * q0;
  115. p0 /= factor;
  116. q0 /= factor;
  117. p1 /= factor;
  118. q1 /= factor;
  119. }
  120. p0 = *x * p1 + di * p0; // y[k+2] = x * y[k+1] + (k+2)/2 * y[k] with k even
  121. q0 = *x * q1 + di * q0;
  122. p1 = p0 + (di + 1.0 - *alpha) * p1; // y[k+2] = y[k+1] + ((k+3)/2 - alpha) * y[k] with k odd
  123. q1 = q0 + (di + 1.0 - *alpha) * q1;
  124. } else {
  125. // We should not come here at all!
  126. *result = 0.5 * (r0 + *result);
  127. return;
  128. }
  129. }
  130. // We should not come here at all!
  131. *result = 0.5 * (r0 + *result);
  132. }
  133. static void shiftAlphaByOne (std::complex<double> *alpha, std::complex<double> *x, std::complex<double> *result) {
  134. std::complex<double> one (1.0, 0.0);
  135. integer n = (integer) (real (*alpha) - real (*x));
  136. if (n > 0) {
  137. std::complex<double> cn = n + 1;
  138. std::complex<double> alpha1 = *alpha - cn;
  139. std::complex<double> term = one / *x;
  140. std::complex<double> sum = term;
  141. for (integer i = 1; i <= n; i ++) {
  142. cn = n - i + 1;
  143. term *= (alpha1 + cn) / *x;
  144. sum += term;
  145. }
  146. continuedFractionExpansion (& alpha1, x, result);
  147. sum += term * alpha1 / *result;
  148. *result = one / sum;
  149. } else {
  150. continuedFractionExpansion (alpha, x, result);
  151. }
  152. }
  153. // Gamma[alpha,x] = integral{x, infty, t^(alpha-1)exp(-t)dt}, Gamma[alpha]= Gamma[alpha,0]
  154. dcomplex NUMincompleteGammaFunction (double alpha_re, double alpha_im, double x_re, double x_im) {
  155. std::complex<double> alpha (alpha_re, alpha_im), x (x_re, x_im), result;
  156. double xlim = 1.0;
  157. integer ibuf = 34;
  158. std::complex<double> re = 0.36787944117144232, one = 1.0, p, q, r;
  159. if (norm1 (& x) < xlim || real (x) < 0.0 && fabs (imag (x)) < xlim) {
  160. shiftAlphaByOne (& alpha, & one, & r);
  161. result = re / r;
  162. integer ilim = (integer) real (x / re);
  163. for (integer i = 0; i <= ibuf - ilim; i++) {
  164. xShiftTerm (& alpha, & x, i, & p, & q);
  165. result += p * q;
  166. }
  167. } else {
  168. shiftAlphaByOne (& alpha, & x, & r);
  169. result = exp (-x + alpha * log (x)) / r;
  170. }
  171. return { real (result), imag (result) };
  172. }
  173. // End of translated fortran code
  174. /*
  175. * We have to scale the amplitude "a" of the gammatone such that the response is 0 dB at the peak (w=w0);
  176. *
  177. * To calculate the spectrum of the truncated gammatone
  178. * g(t) = t^(n-1)*exp(-b*t)cos(w0*t+phi) for 0 < t <= T
  179. * g(t) = 0 for t > T
  180. * we can write
  181. * g(t)= t^(n-1)*exp(-b*t)(exp(I*(w0*t+phi))+exp(-I*(w0*t+phi)))/2
  182. * = (gp(t)+gm(t))/2, where
  183. * gp(t) = t^(n-1)*exp(-b*t + I*(w0*t+phi))
  184. * gm(t) = t^(n-1)*exp(-b*t - I*(w0*t+phi))
  185. *
  186. * Laplace[g(t)]= Laplace[g(t)]-Laplace[g(t+T)],where
  187. * g(t+T) = g2(t) = (t+T)^(n-1)*exp(-b*(t+T))cos(w0*(t+T)+phi) for t>0
  188. *
  189. * g2(t) = (t+T)^(n-1)*(exp(-b*(t+T)+I(w0*(t+T)+phi) + exp(-b*(t+T)-I(w0*(t+T)+phi)))/2
  190. * = (exp(-b*T+I*w0*T+I*phi)(t+T)^(n-1)exp(-b*t+I*w0*t) + exp(-b*T-I*w0*T-I*phi)(t+T)^(n-1)exp(-b*t-I*w0*t))/2
  191. * = (gpT(t)+gmT(t))/2, where
  192. * gpT(t)= exp(-b*T+I*w0*T+I*phi) (t+T)^(n-1) exp(-b*t+I*w0*t)
  193. * gmT(t)= exp(-b*T-I*w0*T-I*phi) (t+T)^(n-1) exp(-b*t-I*w0*t)
  194. *
  195. * Fp(w) = Laplace[gp(t), s=Iw] = exp( I*phi) (b+I(w-w0))^-n Gamma[n]
  196. * Fm(w) = Laplace[gm(t), s=Iw] = exp(-I*phi) (b+I(w+w0))^-n Gamma[n]
  197. * FpT(w) = Laplace[gpT(t),s=Iw] = exp( I*phi) exp(I*w*T) (b+I(w-w0))^-n Gamma[n, b*T+I(w-w0)*T]
  198. * FmT(w) = Laplace[gmT(t),s=Iw] = exp(-I*phi) exp(I*w*T) (b+I(w+w0))^-n Gamma[n, b*T+I(w+w0)*T]
  199. * F[g(t)] = (Fp(w) + Fm(w) - FpT(w) - FmT(w)) / 2
  200. *
  201. * At resonance w=w0:
  202. * F(w0) = (Fp(w0) + Fm(w0) - FpT(w0) - FmT(w0)) / 2 =
  203. * = (exp(I*phi) (b)^-n Gamma[n] + exp(-I*phi) (b+I*2*w0)^-n Gamma[n] -
  204. * exp(I*phi) exp(I*w0*T) (b)^-n Gamma[n, b*T] -
  205. * exp(-I*phi) exp(I*w0*T) (b+I*2*w0)^-n Gamma[n, b*T+I*2*w0)]) / 2
  206. * = b^-n(exp(I*phi) Gamma[n] + exp(-I*phi) (1+I*2*w0/b)^-n Gamma[n] -
  207. * exp(I*phi) exp(I*w0*T) Gamma[n, b*T0] - exp(-I*phi) exp(I*w0*T) (1+I*2*w0/b)^-n Gamma[n, b*T+I*2*w0*T)]) / 2
  208. *
  209. * (x+I*y)^-n = (r*exp(I theta))^-n, where r = sqrt(x^2+y^2) and theta = ArcTan (y/x)
  210. * (1+I*a)^-n = (1+a^2)^(-n/2) exp(-I*n*theta)
  211. */
  212. dcomplex gammaToneFilterResponseAtCentreFrequency (double centre_frequency, double bandwidth, double gamma, double initialPhase, double truncationTime) {
  213. double b = NUM2pi * bandwidth, w0 = NUM2pi * centre_frequency, theta = atan (2.0 * centre_frequency / bandwidth);
  214. double gamma_n = exp (NUMlnGamma (gamma)), bpow = pow (b, -gamma);
  215. std::complex<double> expiphi (cos (initialPhase), sin(initialPhase)), expmiphi = conj (expiphi);
  216. std::complex<double> expnitheta (cos (gamma * theta), - sin(gamma * theta));
  217. std::complex<double> expiw0T (cos (w0 * truncationTime), sin (w0 * truncationTime));
  218. std::complex<double> peak = expnitheta * pow (1.0 + 4.0 * (w0 / b) * (w0 / b), - 0.5 * gamma);
  219. dcomplex r1 = NUMincompleteGammaFunction (gamma, 0.0, b * truncationTime, 0.0);
  220. dcomplex r2 = NUMincompleteGammaFunction (gamma, 0.0, b * truncationTime, 2.0 * w0 * truncationTime);
  221. std::complex<double> result1 (r1.re, r1.im), result2 (r2.re, r2.im);
  222. //std::complex<double> result1 (result1_re, result1_im), result2 (result2_re, result2_im);
  223. std::complex<double> response = 0.5 * bpow * ((expiphi + expmiphi * peak) * gamma_n -
  224. expiw0T * (expiphi * result1 + expmiphi * peak * result2));
  225. return { real (response), imag (response) };
  226. }
  227. /* End of file NUMcomplex.cpp */