MathMitLef.Mod 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. (* CAPO - Computational Analysis Platform for Oberon - by Alan Freed and Felix Friedrich. *)
  2. (* Version 1, Update 2 *)
  3. MODULE MathMitLef; (** AUTHOR "adf"; PURPOSE "Mittag-Leffler function"; *)
  4. (** The Mittag-Leffler function is the characteristic solution to fractional-order differential equations,
  5. like the exponential function is the characteristic solution to ordinary differential equations. *)
  6. IMPORT NbrInt, NbrRe, NbrCplx, DataErrors, MathRe, MathGamma, MathCplx, MathCplxSeries, CalcGauss;
  7. VAR
  8. maxIterations: NbrInt.Integer; storeAlpha, storeBeta, tolerance: NbrRe.Real;
  9. yk: ARRAY 2 OF NbrRe.Real;
  10. yp: ARRAY 3 OF NbrRe.Real;
  11. TYPE
  12. MitLef = OBJECT (MathCplxSeries.Coefficient)
  13. (* Series solution for the Mittag-Leffler function. Only apply this as a solution around 0. *)
  14. PROCEDURE Evaluate*;
  15. VAR x: NbrRe.Real;
  16. BEGIN
  17. x := storeBeta + n * storeAlpha;
  18. IF GammaIsSingularAt( x ) THEN coef := 0 ELSE coef := 1 / MathGamma.Fn( x ) END;
  19. IF n = maxIterations THEN eos := TRUE; DataErrors.ReWarning( x, "Did not converge - timed out." ) END
  20. END Evaluate;
  21. END MitLef;
  22. AsympMitLef = OBJECT (MathCplxSeries.Coefficient)
  23. (* Series solution for the Mittag-Leffler function. Only apply this as a solution for large x. *)
  24. PROCEDURE Evaluate*;
  25. VAR x: NbrRe.Real;
  26. BEGIN
  27. x := storeBeta - n * storeAlpha;
  28. IF GammaIsSingularAt( x ) THEN coef := 0 ELSE coef := 1 / MathGamma.Fn( x ) END;
  29. IF n = maxIterations THEN eos := TRUE; DataErrors.ReWarning( x, "Did not converge - timed out." ) END
  30. END Evaluate;
  31. END AsympMitLef;
  32. DMitLef = OBJECT (MathCplxSeries.Coefficient)
  33. (* Series solution for the derivative of the Mittag-Leffler function. Only apply this as a solution around 0. *)
  34. PROCEDURE Evaluate*;
  35. VAR x: NbrRe.Real;
  36. BEGIN
  37. x := (1 + n) * storeAlpha + storeBeta;
  38. IF GammaIsSingularAt( x ) THEN coef := 0 ELSE coef := (1 + n) / MathGamma.Fn( x ) END;
  39. IF n = maxIterations THEN eos := TRUE; DataErrors.ReWarning( x, "Did not converge - timed out." ) END
  40. END Evaluate;
  41. END DMitLef;
  42. PROCEDURE K( x: NbrRe.Real; z: NbrCplx.Complex ): NbrCplx.Complex;
  43. (* Use the mappings: x , c, yk[0] , a, yk[1] , b, z , z. *)
  44. VAR coef1, coef2, r1: NbrRe.Real; answer, denom, numer: NbrCplx.Complex;
  45. BEGIN
  46. coef1 := MathRe.Power( x, (1 - yk[1]) / yk[0] ) / (NbrRe.Pi * yk[0]);
  47. coef2 := MathRe.Exp( -MathRe.Power( x, 1 / yk[0] ) ); r1 := NbrRe.Pi * (1 - yk[1]);
  48. numer := x * MathRe.Sin( r1 ) - z * MathRe.Sin( r1 + NbrRe.Pi * yk[0] );
  49. denom := x * x - 2 * x * z * MathRe.Cos( NbrRe.Pi * yk[0] ) + z * z; answer := coef1 * coef2 * numer / denom;
  50. RETURN answer
  51. END K;
  52. PROCEDURE P( x: NbrRe.Real; z: NbrCplx.Complex ): NbrCplx.Complex;
  53. (* Use the mappings: x , f, yp[0] , a, yp[1] , b, yp[2] , e, z , z. *)
  54. VAR coef1, coef2, r1: NbrRe.Real; answer, denom, numer: NbrCplx.Complex;
  55. BEGIN
  56. coef1 := MathRe.Power( yp[2], 1 + (1 - yp[1]) / yp[0] ) / (2 * NbrRe.Pi * yp[0]);
  57. coef2 := MathRe.Exp( MathRe.Power( yp[2], 1 / yp[0] ) * MathRe.Cos( x / yp[0] ) );
  58. r1 := x * (1 + (1 - yp[1]) / yp[0]) + MathRe.Power( yp[2], 1 / yp[0] ) * MathRe.Sin( x / yp[0] );
  59. numer := MathRe.Cos( r1 ) + NbrCplx.I * MathRe.Sin( r1 ); denom := yp[2] * MathCplx.Exp( NbrCplx.I * x ) - z;
  60. answer := coef1 * coef2 * numer / denom; RETURN answer
  61. END P;
  62. PROCEDURE GammaIsSingularAt( x: NbrRe.Real ): BOOLEAN;
  63. BEGIN
  64. IF x > 0 THEN RETURN FALSE
  65. ELSIF x < 0 THEN
  66. IF x = NbrRe.Int( x ) + 1 THEN RETURN TRUE ELSE RETURN FALSE END
  67. ELSE RETURN TRUE
  68. END
  69. END GammaIsSingularAt;
  70. (** The Mittag-Leffler function, Ea,b(z) = ek=0% zk/G(ak+b), a, b N R, a > 0, z N C. *)
  71. PROCEDURE Fn*( alpha, beta, x: NbrRe.Real ): NbrRe.Real;
  72. VAR abs, answer, arg: NbrRe.Real; ml, z: NbrCplx.Complex;
  73. BEGIN
  74. z := x; ml := CplxFn( alpha, beta, z ); abs := NbrCplx.Abs( ml ); arg := NbrCplx.Arg( ml );
  75. IF (arg < tolerance) & (-tolerance < arg) THEN answer := abs
  76. ELSIF (arg > NbrRe.Pi - tolerance) OR (arg < -NbrRe.Pi + tolerance) THEN answer := -abs
  77. ELSE DataErrors.Error( "The result is complex. Use CplxFn instead." )
  78. END;
  79. RETURN answer
  80. END Fn;
  81. PROCEDURE CplxFn*( alpha, beta: NbrRe.Real; z: NbrCplx.Complex ): NbrCplx.Complex;
  82. VAR i1, i2, k, k0, res: NbrInt.Integer; a, absZ, argZ, b, r1, r2, x0: NbrRe.Real;
  83. answer, c1, c2: NbrCplx.Complex; ml: MitLef; aml: AsympMitLef;
  84. BEGIN
  85. (* Implements the algorithm of R. Gorenflo, I. Loutchko and Yu. Luchko, "Computation of the Mittag-Leffler
  86. function Ea,b(z) and its derivatives," Fractional Calculus and Applied Analysis, 5 (2002), 491-518.. *)
  87. alpha := NbrRe.Abs( alpha ); absZ := NbrCplx.Abs( z ); argZ := NbrCplx.Arg( z );
  88. IF absZ < 1 / NbrRe.MaxNbr THEN
  89. IF GammaIsSingularAt( beta ) THEN answer := 0 ELSE answer := 1 / MathGamma.Fn( beta ) END
  90. ELSIF (alpha = 1) & (beta = 1) THEN
  91. answer := MathCplx.Exp( z )
  92. ELSIF 1 < alpha THEN
  93. k0 := NbrRe.Floor( alpha ) + 1; c1 := (2 * NbrRe.Pi / k0) * NbrCplx.I; r1 := 1 / k0; r2 := alpha / k0; answer := 0;
  94. FOR k := 0 TO k0 - 1 DO
  95. c2 := MathCplx.RealPower( z, r1 ) * MathCplx.Exp( c1 * k ); answer := answer + CplxFn( r2, beta, c2 )
  96. END;
  97. answer := answer / k0
  98. ELSE
  99. IF absZ < 0.9 THEN BEGIN {EXCLUSIVE}
  100. storeAlpha := alpha; storeBeta := beta; i1 := NbrRe.Ceiling( (1 - beta) / alpha );
  101. i2 := NbrRe.Ceiling( MathRe.Ln( NbrRe.Epsilon * (1 - absZ) ) / MathRe.Ln( absZ ) );
  102. maxIterations := NbrInt.Max( i1, i2 ); NEW( ml ); answer := MathCplxSeries.PowerSeries( ml, z ) END
  103. ELSIF absZ < NbrRe.Floor( 10 + 5 * alpha ) THEN
  104. IF beta >= 0 THEN
  105. r1 := 1; x0 := NbrRe.Max( r1, 2 * absZ );
  106. x0 := NbrRe.Max( x0, MathRe.Power( -MathRe.Ln( NbrRe.Epsilon * NbrRe.Pi / 6 ), alpha ) )
  107. ELSE
  108. r1 := NbrRe.Abs( beta ); r2 := MathRe.Power( 1 + r1, alpha ); x0 := NbrRe.Max( r2, 2 * absZ );
  109. r2 := 6 * (2 + r1) * MathRe.Power( 2 * r1, r1 );
  110. x0 := NbrRe.Max( x0, MathRe.Power( -2 * MathRe.Ln( NbrRe.Epsilon * NbrRe.Pi / r2 ), alpha ) )
  111. END;
  112. IF (NbrRe.Abs( argZ ) > alpha * NbrRe.Pi) &
  113. (NbrRe.Abs(NbrRe.Abs( argZ ) - alpha * NbrRe.Pi) > NbrRe.Epsilon) THEN
  114. IF beta < 1 + alpha THEN
  115. BEGIN {EXCLUSIVE}
  116. a := 0; b := x0; yk[0] := alpha; yk[1] := beta;
  117. answer := CalcGauss.SolveCplx( K, a, b, z, CalcGauss.Medium, tolerance, res ) END;
  118. IF res # CalcGauss.OKay THEN
  119. IF res = CalcGauss.MaxSubDivReached THEN
  120. DataErrors.Warning( "Maximum subdivisions reached when integrating K." )
  121. ELSIF res = CalcGauss.RoundoffError THEN
  122. DataErrors.Warning( "Excessive roundoff error occurred when integrating K." )
  123. ELSIF res = CalcGauss.RoughIntegrand THEN
  124. DataErrors.Warning( "A rough integrand encountered when integrating K." )
  125. ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
  126. END
  127. END
  128. ELSE
  129. BEGIN {EXCLUSIVE}
  130. a := 1; b := x0; yk[0] := alpha; yk[1] := beta;
  131. answer := CalcGauss.SolveCplx( K, a, b, z, CalcGauss.Medium, tolerance, res ) END;
  132. IF res # CalcGauss.OKay THEN
  133. IF res = CalcGauss.MaxSubDivReached THEN
  134. DataErrors.Warning( "Maximum subdivisions reached when integrating K." )
  135. ELSIF res = CalcGauss.RoundoffError THEN
  136. DataErrors.Warning( "Excessive roundoff error occurred when integrating K." )
  137. ELSIF res = CalcGauss.RoughIntegrand THEN
  138. DataErrors.Warning( "A rough integrand encountered when integrating K." )
  139. ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
  140. END
  141. END; BEGIN {EXCLUSIVE}
  142. a := -alpha * NbrRe.Pi; b := -a; yp[0] := alpha; yp[1] := beta; yp[2] := 1;
  143. answer := answer + CalcGauss.SolveCplx( P, a, b, z, CalcGauss.Fine, tolerance, res ) END;
  144. IF res # CalcGauss.OKay THEN
  145. IF res = CalcGauss.MaxSubDivReached THEN
  146. DataErrors.Warning( "Maximum subdivisions reached when integrating P." )
  147. ELSIF res = CalcGauss.RoundoffError THEN
  148. DataErrors.Warning( "Excessive roundoff error occurred when integrating P." )
  149. ELSIF res = CalcGauss.RoughIntegrand THEN
  150. DataErrors.Warning( "A rough integrand encountered when integrating P." )
  151. ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
  152. END
  153. END
  154. END
  155. ELSIF (NbrRe.Abs( argZ ) < alpha * NbrRe.Pi) &
  156. (NbrRe.Abs(NbrRe.Abs( argZ ) - alpha * NbrRe.Pi) > NbrRe.Epsilon) THEN
  157. IF beta < 1 + alpha THEN
  158. BEGIN {EXCLUSIVE}
  159. a := 0; b := x0; yk[0] := alpha; yk[1] := beta;
  160. answer := CalcGauss.SolveCplx( K, a, b, z, CalcGauss.Medium, tolerance, res ) END;
  161. IF res # CalcGauss.OKay THEN
  162. IF res = CalcGauss.MaxSubDivReached THEN
  163. DataErrors.Warning( "Maximum subdivisions reached when integrating K." )
  164. ELSIF res = CalcGauss.RoundoffError THEN
  165. DataErrors.Warning( "Excessive roundoff error occurred when integrating K." )
  166. ELSIF res = CalcGauss.RoughIntegrand THEN
  167. DataErrors.Warning( "A rough integrand encountered when integrating K." )
  168. ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
  169. END
  170. END
  171. ELSE
  172. BEGIN {EXCLUSIVE}
  173. a := absZ / 2; b := x0; yk[0] := alpha; yk[1] := beta;
  174. answer := CalcGauss.SolveCplx( K, a, b, z, CalcGauss.Medium, tolerance, res ) END;
  175. IF res # CalcGauss.OKay THEN
  176. IF res = CalcGauss.MaxSubDivReached THEN
  177. DataErrors.Warning( "Maximum subdivisions reached when integrating K." )
  178. ELSIF res = CalcGauss.RoundoffError THEN
  179. DataErrors.Warning( "Excessive roundoff error occurred when integrating K." )
  180. ELSIF res = CalcGauss.RoughIntegrand THEN
  181. DataErrors.Warning( "A rough integrand encountered when integrating K." )
  182. ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
  183. END
  184. END; BEGIN {EXCLUSIVE}
  185. a := -alpha * NbrRe.Pi; b := -a; yp[0] := alpha; yp[1] := beta; yp[2] := absZ / 2;
  186. answer := answer + CalcGauss.SolveCplx( P, a, b, z, CalcGauss.Fine, tolerance, res ) END;
  187. IF res # CalcGauss.OKay THEN
  188. IF res = CalcGauss.MaxSubDivReached THEN
  189. DataErrors.Warning( "Maximum subdivisions reached when integrating P." )
  190. ELSIF res = CalcGauss.RoundoffError THEN
  191. DataErrors.Warning( "Excessive roundoff error occurred when integrating P." )
  192. ELSIF res = CalcGauss.RoughIntegrand THEN
  193. DataErrors.Warning( "A rough integrand encountered when integrating P." )
  194. ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
  195. END
  196. END
  197. END;
  198. answer :=
  199. answer +
  200. MathCplx.RealPower( z, (1 - beta) / alpha ) *
  201. MathCplx.Exp( MathCplx.RealPower( z, 1 / alpha ) ) / alpha
  202. ELSE
  203. BEGIN {EXCLUSIVE}
  204. a := (absZ + 1) / 2; b := x0; yk[0] := alpha; yk[1] := beta;
  205. answer := CalcGauss.SolveCplx( K, a, b, z, CalcGauss.Medium, tolerance, res ) END;
  206. IF res # CalcGauss.OKay THEN
  207. IF res = CalcGauss.MaxSubDivReached THEN
  208. DataErrors.Warning( "Maximum subdivisions reached when integrating K." )
  209. ELSIF res = CalcGauss.RoundoffError THEN
  210. DataErrors.Warning( "Excessive roundoff error occurred when integrating K." )
  211. ELSIF res = CalcGauss.RoughIntegrand THEN
  212. DataErrors.Warning( "A rough integrand encountered when integrating K." )
  213. ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
  214. END
  215. END; BEGIN {EXCLUSIVE}
  216. a := -alpha * NbrRe.Pi; b := -a; yp[0] := alpha; yp[1] := beta; yp[2] := (absZ + 1) / 2;
  217. answer := answer + CalcGauss.SolveCplx( P, a, b, z, CalcGauss.Fine, tolerance, res ) END;
  218. IF res # CalcGauss.OKay THEN
  219. IF res = CalcGauss.MaxSubDivReached THEN
  220. DataErrors.Warning( "Maximum subdivisions reached when integrating P." )
  221. ELSIF res = CalcGauss.RoundoffError THEN
  222. DataErrors.Warning( "Excessive roundoff error occurred when integrating P." )
  223. ELSIF res = CalcGauss.RoughIntegrand THEN
  224. DataErrors.Warning( "A rough integrand encountered when integrating P." )
  225. ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
  226. END
  227. END
  228. END
  229. ELSE (* absZ > NbrRe.Floor( 10 + 5*alpha ) *)
  230. IF argZ < 3 * alpha * NbrRe.Pi / 4 THEN
  231. answer :=
  232. MathCplx.RealPower( z, (1 - beta) / alpha ) *
  233. MathCplx.Exp( MathCplx.RealPower( z, 1 / alpha ) ) / alpha
  234. ELSE answer := 0
  235. END; BEGIN {EXCLUSIVE}
  236. storeAlpha := alpha; storeBeta := beta;
  237. (* Factor of 2 put in for 32-bit precision - needed to assure convergence. *)
  238. maxIterations := 2 * NbrRe.Floor( -MathRe.Ln( NbrRe.Epsilon ) / MathRe.Ln( absZ ) ); NEW( aml );
  239. answer := answer - MathCplxSeries.PowerSeries( aml, 1 / z );
  240. (* Remove the zeroth term from the sum, because the actual starts at one. *)
  241. IF ~GammaIsSingularAt( beta ) THEN answer := answer + 1 / MathGamma.Fn( beta ) END END
  242. END
  243. END;
  244. RETURN answer
  245. END CplxFn;
  246. (** The derivative of the Mittag-Leffler function, dEa,b(z)/dz = ek=0% (1+k)zk/G(a(1+k)+b). *)
  247. PROCEDURE DFn*( alpha, beta, x: NbrRe.Real ): NbrRe.Real;
  248. VAR abs, answer, arg: NbrRe.Real; dml, z: NbrCplx.Complex;
  249. BEGIN
  250. z := x; dml := DCplxFn( alpha, beta, z ); abs := NbrCplx.Abs( dml ); arg := NbrCplx.Arg( dml );
  251. IF (arg < tolerance) & (-tolerance < arg) THEN answer := abs
  252. ELSIF (arg > NbrRe.Pi - tolerance) OR (arg < -NbrRe.Pi + tolerance) THEN answer := -abs
  253. ELSE DataErrors.Error( "The result is complex. Use DCplxFn instead." )
  254. END;
  255. RETURN answer
  256. END DFn;
  257. PROCEDURE DCplxFn*( alpha, beta: NbrRe.Real; z: NbrCplx.Complex ): NbrCplx.Complex;
  258. VAR answer: NbrCplx.Complex; absZ, d, k, k1, omega: NbrRe.Real; k0: NbrInt.Integer; dml: DMitLef;
  259. BEGIN
  260. alpha := NbrRe.Abs( alpha ); absZ := NbrCplx.Abs( z );
  261. IF absZ < 1 / NbrRe.MaxNbr THEN
  262. IF GammaIsSingularAt( alpha + beta ) THEN answer := 0 ELSE answer := 1 / MathGamma.Fn( alpha + beta ) END
  263. ELSIF absZ < 0.9 THEN
  264. IF alpha > 1 THEN k1 := 1 + (2 - alpha - beta) / (alpha - 1)
  265. ELSE
  266. d := 1 + alpha * (alpha - 4 * beta + 6); k := 1 + (3 - alpha - beta) / alpha;
  267. IF d <= 1 THEN k1 := k
  268. ELSE
  269. omega := alpha + beta - 1.5;
  270. k1 := NbrRe.Max( k, 1 + (1 - 2 * omega * alpha + MathRe.Sqrt( d )) / (2 * alpha * alpha) )
  271. END
  272. END; BEGIN {EXCLUSIVE}
  273. k0 := NbrRe.Ceiling( NbrRe.Max( k1, MathRe.Ln( NbrRe.Epsilon * (1 - absZ) ) ) / MathRe.Ln( absZ ) );
  274. maxIterations := k0; storeAlpha := alpha; storeBeta := beta; NEW( dml );
  275. answer := MathCplxSeries.PowerSeries( dml, z ) END
  276. ELSE
  277. answer := CplxFn( alpha, beta - 1, z );
  278. IF beta # 1 THEN answer := answer - (beta - 1) * CplxFn( alpha, beta, z ) END;
  279. answer := answer / (alpha * z)
  280. END;
  281. RETURN answer
  282. END DCplxFn;
  283. BEGIN
  284. tolerance := MathRe.Sqrt( NbrRe.Epsilon )
  285. END MathMitLef.