123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- (* CAPO - Computational Analysis Platform for Oberon - by Alan Freed and Felix Friedrich. *)
- (* Version 1, Update 2 *)
- MODULE MathMitLef; (** AUTHOR "adf"; PURPOSE "Mittag-Leffler function"; *)
- (** The Mittag-Leffler function is the characteristic solution to fractional-order differential equations,
- like the exponential function is the characteristic solution to ordinary differential equations. *)
- IMPORT NbrInt, NbrRe, NbrCplx, DataErrors, MathRe, MathGamma, MathCplx, MathCplxSeries, CalcGauss;
- VAR
- maxIterations: NbrInt.Integer; storeAlpha, storeBeta, tolerance: NbrRe.Real;
- yk: ARRAY 2 OF NbrRe.Real;
- yp: ARRAY 3 OF NbrRe.Real;
- TYPE
- MitLef = OBJECT (MathCplxSeries.Coefficient)
- (* Series solution for the Mittag-Leffler function. Only apply this as a solution around 0. *)
- PROCEDURE Evaluate*;
- VAR x: NbrRe.Real;
- BEGIN
- x := storeBeta + n * storeAlpha;
- IF GammaIsSingularAt( x ) THEN coef := 0 ELSE coef := 1 / MathGamma.Fn( x ) END;
- IF n = maxIterations THEN eos := TRUE; DataErrors.ReWarning( x, "Did not converge - timed out." ) END
- END Evaluate;
- END MitLef;
- AsympMitLef = OBJECT (MathCplxSeries.Coefficient)
- (* Series solution for the Mittag-Leffler function. Only apply this as a solution for large x. *)
- PROCEDURE Evaluate*;
- VAR x: NbrRe.Real;
- BEGIN
- x := storeBeta - n * storeAlpha;
- IF GammaIsSingularAt( x ) THEN coef := 0 ELSE coef := 1 / MathGamma.Fn( x ) END;
- IF n = maxIterations THEN eos := TRUE; DataErrors.ReWarning( x, "Did not converge - timed out." ) END
- END Evaluate;
- END AsympMitLef;
- DMitLef = OBJECT (MathCplxSeries.Coefficient)
- (* Series solution for the derivative of the Mittag-Leffler function. Only apply this as a solution around 0. *)
- PROCEDURE Evaluate*;
- VAR x: NbrRe.Real;
- BEGIN
- x := (1 + n) * storeAlpha + storeBeta;
- IF GammaIsSingularAt( x ) THEN coef := 0 ELSE coef := (1 + n) / MathGamma.Fn( x ) END;
- IF n = maxIterations THEN eos := TRUE; DataErrors.ReWarning( x, "Did not converge - timed out." ) END
- END Evaluate;
- END DMitLef;
- PROCEDURE K( x: NbrRe.Real; z: NbrCplx.Complex ): NbrCplx.Complex;
- (* Use the mappings: x , c, yk[0] , a, yk[1] , b, z , z. *)
- VAR coef1, coef2, r1: NbrRe.Real; answer, denom, numer: NbrCplx.Complex;
- BEGIN
- coef1 := MathRe.Power( x, (1 - yk[1]) / yk[0] ) / (NbrRe.Pi * yk[0]);
- coef2 := MathRe.Exp( -MathRe.Power( x, 1 / yk[0] ) ); r1 := NbrRe.Pi * (1 - yk[1]);
- numer := x * MathRe.Sin( r1 ) - z * MathRe.Sin( r1 + NbrRe.Pi * yk[0] );
- denom := x * x - 2 * x * z * MathRe.Cos( NbrRe.Pi * yk[0] ) + z * z; answer := coef1 * coef2 * numer / denom;
- RETURN answer
- END K;
- PROCEDURE P( x: NbrRe.Real; z: NbrCplx.Complex ): NbrCplx.Complex;
- (* Use the mappings: x , f, yp[0] , a, yp[1] , b, yp[2] , e, z , z. *)
- VAR coef1, coef2, r1: NbrRe.Real; answer, denom, numer: NbrCplx.Complex;
- BEGIN
- coef1 := MathRe.Power( yp[2], 1 + (1 - yp[1]) / yp[0] ) / (2 * NbrRe.Pi * yp[0]);
- coef2 := MathRe.Exp( MathRe.Power( yp[2], 1 / yp[0] ) * MathRe.Cos( x / yp[0] ) );
- r1 := x * (1 + (1 - yp[1]) / yp[0]) + MathRe.Power( yp[2], 1 / yp[0] ) * MathRe.Sin( x / yp[0] );
- numer := MathRe.Cos( r1 ) + NbrCplx.I * MathRe.Sin( r1 ); denom := yp[2] * MathCplx.Exp( NbrCplx.I * x ) - z;
- answer := coef1 * coef2 * numer / denom; RETURN answer
- END P;
- PROCEDURE GammaIsSingularAt( x: NbrRe.Real ): BOOLEAN;
- BEGIN
- IF x > 0 THEN RETURN FALSE
- ELSIF x < 0 THEN
- IF x = NbrRe.Int( x ) + 1 THEN RETURN TRUE ELSE RETURN FALSE END
- ELSE RETURN TRUE
- END
- END GammaIsSingularAt;
- (** The Mittag-Leffler function, Ea,b(z) = ek=0% zk/G(ak+b), a, b N R, a > 0, z N C. *)
- PROCEDURE Fn*( alpha, beta, x: NbrRe.Real ): NbrRe.Real;
- VAR abs, answer, arg: NbrRe.Real; ml, z: NbrCplx.Complex;
- BEGIN
- z := x; ml := CplxFn( alpha, beta, z ); abs := NbrCplx.Abs( ml ); arg := NbrCplx.Arg( ml );
- IF (arg < tolerance) & (-tolerance < arg) THEN answer := abs
- ELSIF (arg > NbrRe.Pi - tolerance) OR (arg < -NbrRe.Pi + tolerance) THEN answer := -abs
- ELSE DataErrors.Error( "The result is complex. Use CplxFn instead." )
- END;
- RETURN answer
- END Fn;
- PROCEDURE CplxFn*( alpha, beta: NbrRe.Real; z: NbrCplx.Complex ): NbrCplx.Complex;
- VAR i1, i2, k, k0, res: NbrInt.Integer; a, absZ, argZ, b, r1, r2, x0: NbrRe.Real;
- answer, c1, c2: NbrCplx.Complex; ml: MitLef; aml: AsympMitLef;
- BEGIN
- (* Implements the algorithm of R. Gorenflo, I. Loutchko and Yu. Luchko, "Computation of the Mittag-Leffler
- function Ea,b(z) and its derivatives," Fractional Calculus and Applied Analysis, 5 (2002), 491-518.. *)
- alpha := NbrRe.Abs( alpha ); absZ := NbrCplx.Abs( z ); argZ := NbrCplx.Arg( z );
- IF absZ < 1 / NbrRe.MaxNbr THEN
- IF GammaIsSingularAt( beta ) THEN answer := 0 ELSE answer := 1 / MathGamma.Fn( beta ) END
- ELSIF (alpha = 1) & (beta = 1) THEN
- answer := MathCplx.Exp( z )
- ELSIF 1 < alpha THEN
- k0 := NbrRe.Floor( alpha ) + 1; c1 := (2 * NbrRe.Pi / k0) * NbrCplx.I; r1 := 1 / k0; r2 := alpha / k0; answer := 0;
- FOR k := 0 TO k0 - 1 DO
- c2 := MathCplx.RealPower( z, r1 ) * MathCplx.Exp( c1 * k ); answer := answer + CplxFn( r2, beta, c2 )
- END;
- answer := answer / k0
- ELSE
- IF absZ < 0.9 THEN BEGIN {EXCLUSIVE}
- storeAlpha := alpha; storeBeta := beta; i1 := NbrRe.Ceiling( (1 - beta) / alpha );
- i2 := NbrRe.Ceiling( MathRe.Ln( NbrRe.Epsilon * (1 - absZ) ) / MathRe.Ln( absZ ) );
- maxIterations := NbrInt.Max( i1, i2 ); NEW( ml ); answer := MathCplxSeries.PowerSeries( ml, z ) END
- ELSIF absZ < NbrRe.Floor( 10 + 5 * alpha ) THEN
- IF beta >= 0 THEN
- r1 := 1; x0 := NbrRe.Max( r1, 2 * absZ );
- x0 := NbrRe.Max( x0, MathRe.Power( -MathRe.Ln( NbrRe.Epsilon * NbrRe.Pi / 6 ), alpha ) )
- ELSE
- r1 := NbrRe.Abs( beta ); r2 := MathRe.Power( 1 + r1, alpha ); x0 := NbrRe.Max( r2, 2 * absZ );
- r2 := 6 * (2 + r1) * MathRe.Power( 2 * r1, r1 );
- x0 := NbrRe.Max( x0, MathRe.Power( -2 * MathRe.Ln( NbrRe.Epsilon * NbrRe.Pi / r2 ), alpha ) )
- END;
- IF (NbrRe.Abs( argZ ) > alpha * NbrRe.Pi) &
- (NbrRe.Abs(NbrRe.Abs( argZ ) - alpha * NbrRe.Pi) > NbrRe.Epsilon) THEN
- IF beta < 1 + alpha THEN
- BEGIN {EXCLUSIVE}
- a := 0; b := x0; yk[0] := alpha; yk[1] := beta;
- answer := CalcGauss.SolveCplx( K, a, b, z, CalcGauss.Medium, tolerance, res ) END;
- IF res # CalcGauss.OKay THEN
- IF res = CalcGauss.MaxSubDivReached THEN
- DataErrors.Warning( "Maximum subdivisions reached when integrating K." )
- ELSIF res = CalcGauss.RoundoffError THEN
- DataErrors.Warning( "Excessive roundoff error occurred when integrating K." )
- ELSIF res = CalcGauss.RoughIntegrand THEN
- DataErrors.Warning( "A rough integrand encountered when integrating K." )
- ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
- END
- END
- ELSE
- BEGIN {EXCLUSIVE}
- a := 1; b := x0; yk[0] := alpha; yk[1] := beta;
- answer := CalcGauss.SolveCplx( K, a, b, z, CalcGauss.Medium, tolerance, res ) END;
- IF res # CalcGauss.OKay THEN
- IF res = CalcGauss.MaxSubDivReached THEN
- DataErrors.Warning( "Maximum subdivisions reached when integrating K." )
- ELSIF res = CalcGauss.RoundoffError THEN
- DataErrors.Warning( "Excessive roundoff error occurred when integrating K." )
- ELSIF res = CalcGauss.RoughIntegrand THEN
- DataErrors.Warning( "A rough integrand encountered when integrating K." )
- ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
- END
- END; BEGIN {EXCLUSIVE}
- a := -alpha * NbrRe.Pi; b := -a; yp[0] := alpha; yp[1] := beta; yp[2] := 1;
- answer := answer + CalcGauss.SolveCplx( P, a, b, z, CalcGauss.Fine, tolerance, res ) END;
- IF res # CalcGauss.OKay THEN
- IF res = CalcGauss.MaxSubDivReached THEN
- DataErrors.Warning( "Maximum subdivisions reached when integrating P." )
- ELSIF res = CalcGauss.RoundoffError THEN
- DataErrors.Warning( "Excessive roundoff error occurred when integrating P." )
- ELSIF res = CalcGauss.RoughIntegrand THEN
- DataErrors.Warning( "A rough integrand encountered when integrating P." )
- ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
- END
- END
- END
- ELSIF (NbrRe.Abs( argZ ) < alpha * NbrRe.Pi) &
- (NbrRe.Abs(NbrRe.Abs( argZ ) - alpha * NbrRe.Pi) > NbrRe.Epsilon) THEN
- IF beta < 1 + alpha THEN
- BEGIN {EXCLUSIVE}
- a := 0; b := x0; yk[0] := alpha; yk[1] := beta;
- answer := CalcGauss.SolveCplx( K, a, b, z, CalcGauss.Medium, tolerance, res ) END;
- IF res # CalcGauss.OKay THEN
- IF res = CalcGauss.MaxSubDivReached THEN
- DataErrors.Warning( "Maximum subdivisions reached when integrating K." )
- ELSIF res = CalcGauss.RoundoffError THEN
- DataErrors.Warning( "Excessive roundoff error occurred when integrating K." )
- ELSIF res = CalcGauss.RoughIntegrand THEN
- DataErrors.Warning( "A rough integrand encountered when integrating K." )
- ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
- END
- END
- ELSE
- BEGIN {EXCLUSIVE}
- a := absZ / 2; b := x0; yk[0] := alpha; yk[1] := beta;
- answer := CalcGauss.SolveCplx( K, a, b, z, CalcGauss.Medium, tolerance, res ) END;
- IF res # CalcGauss.OKay THEN
- IF res = CalcGauss.MaxSubDivReached THEN
- DataErrors.Warning( "Maximum subdivisions reached when integrating K." )
- ELSIF res = CalcGauss.RoundoffError THEN
- DataErrors.Warning( "Excessive roundoff error occurred when integrating K." )
- ELSIF res = CalcGauss.RoughIntegrand THEN
- DataErrors.Warning( "A rough integrand encountered when integrating K." )
- ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
- END
- END; BEGIN {EXCLUSIVE}
- a := -alpha * NbrRe.Pi; b := -a; yp[0] := alpha; yp[1] := beta; yp[2] := absZ / 2;
- answer := answer + CalcGauss.SolveCplx( P, a, b, z, CalcGauss.Fine, tolerance, res ) END;
- IF res # CalcGauss.OKay THEN
- IF res = CalcGauss.MaxSubDivReached THEN
- DataErrors.Warning( "Maximum subdivisions reached when integrating P." )
- ELSIF res = CalcGauss.RoundoffError THEN
- DataErrors.Warning( "Excessive roundoff error occurred when integrating P." )
- ELSIF res = CalcGauss.RoughIntegrand THEN
- DataErrors.Warning( "A rough integrand encountered when integrating P." )
- ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
- END
- END
- END;
- answer :=
- answer +
- MathCplx.RealPower( z, (1 - beta) / alpha ) *
- MathCplx.Exp( MathCplx.RealPower( z, 1 / alpha ) ) / alpha
- ELSE
- BEGIN {EXCLUSIVE}
- a := (absZ + 1) / 2; b := x0; yk[0] := alpha; yk[1] := beta;
- answer := CalcGauss.SolveCplx( K, a, b, z, CalcGauss.Medium, tolerance, res ) END;
- IF res # CalcGauss.OKay THEN
- IF res = CalcGauss.MaxSubDivReached THEN
- DataErrors.Warning( "Maximum subdivisions reached when integrating K." )
- ELSIF res = CalcGauss.RoundoffError THEN
- DataErrors.Warning( "Excessive roundoff error occurred when integrating K." )
- ELSIF res = CalcGauss.RoughIntegrand THEN
- DataErrors.Warning( "A rough integrand encountered when integrating K." )
- ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
- END
- END; BEGIN {EXCLUSIVE}
- a := -alpha * NbrRe.Pi; b := -a; yp[0] := alpha; yp[1] := beta; yp[2] := (absZ + 1) / 2;
- answer := answer + CalcGauss.SolveCplx( P, a, b, z, CalcGauss.Fine, tolerance, res ) END;
- IF res # CalcGauss.OKay THEN
- IF res = CalcGauss.MaxSubDivReached THEN
- DataErrors.Warning( "Maximum subdivisions reached when integrating P." )
- ELSIF res = CalcGauss.RoundoffError THEN
- DataErrors.Warning( "Excessive roundoff error occurred when integrating P." )
- ELSIF res = CalcGauss.RoughIntegrand THEN
- DataErrors.Warning( "A rough integrand encountered when integrating P." )
- ELSE DataErrors.Error( "Unknown error originating from CalcGauss.SolveCplx." )
- END
- END
- END
- ELSE (* absZ > NbrRe.Floor( 10 + 5*alpha ) *)
- IF argZ < 3 * alpha * NbrRe.Pi / 4 THEN
- answer :=
- MathCplx.RealPower( z, (1 - beta) / alpha ) *
- MathCplx.Exp( MathCplx.RealPower( z, 1 / alpha ) ) / alpha
- ELSE answer := 0
- END; BEGIN {EXCLUSIVE}
- storeAlpha := alpha; storeBeta := beta;
- (* Factor of 2 put in for 32-bit precision - needed to assure convergence. *)
- maxIterations := 2 * NbrRe.Floor( -MathRe.Ln( NbrRe.Epsilon ) / MathRe.Ln( absZ ) ); NEW( aml );
- answer := answer - MathCplxSeries.PowerSeries( aml, 1 / z );
- (* Remove the zeroth term from the sum, because the actual starts at one. *)
- IF ~GammaIsSingularAt( beta ) THEN answer := answer + 1 / MathGamma.Fn( beta ) END END
- END
- END;
- RETURN answer
- END CplxFn;
- (** The derivative of the Mittag-Leffler function, dEa,b(z)/dz = ek=0% (1+k)zk/G(a(1+k)+b). *)
- PROCEDURE DFn*( alpha, beta, x: NbrRe.Real ): NbrRe.Real;
- VAR abs, answer, arg: NbrRe.Real; dml, z: NbrCplx.Complex;
- BEGIN
- z := x; dml := DCplxFn( alpha, beta, z ); abs := NbrCplx.Abs( dml ); arg := NbrCplx.Arg( dml );
- IF (arg < tolerance) & (-tolerance < arg) THEN answer := abs
- ELSIF (arg > NbrRe.Pi - tolerance) OR (arg < -NbrRe.Pi + tolerance) THEN answer := -abs
- ELSE DataErrors.Error( "The result is complex. Use DCplxFn instead." )
- END;
- RETURN answer
- END DFn;
- PROCEDURE DCplxFn*( alpha, beta: NbrRe.Real; z: NbrCplx.Complex ): NbrCplx.Complex;
- VAR answer: NbrCplx.Complex; absZ, d, k, k1, omega: NbrRe.Real; k0: NbrInt.Integer; dml: DMitLef;
- BEGIN
- alpha := NbrRe.Abs( alpha ); absZ := NbrCplx.Abs( z );
- IF absZ < 1 / NbrRe.MaxNbr THEN
- IF GammaIsSingularAt( alpha + beta ) THEN answer := 0 ELSE answer := 1 / MathGamma.Fn( alpha + beta ) END
- ELSIF absZ < 0.9 THEN
- IF alpha > 1 THEN k1 := 1 + (2 - alpha - beta) / (alpha - 1)
- ELSE
- d := 1 + alpha * (alpha - 4 * beta + 6); k := 1 + (3 - alpha - beta) / alpha;
- IF d <= 1 THEN k1 := k
- ELSE
- omega := alpha + beta - 1.5;
- k1 := NbrRe.Max( k, 1 + (1 - 2 * omega * alpha + MathRe.Sqrt( d )) / (2 * alpha * alpha) )
- END
- END; BEGIN {EXCLUSIVE}
- k0 := NbrRe.Ceiling( NbrRe.Max( k1, MathRe.Ln( NbrRe.Epsilon * (1 - absZ) ) ) / MathRe.Ln( absZ ) );
- maxIterations := k0; storeAlpha := alpha; storeBeta := beta; NEW( dml );
- answer := MathCplxSeries.PowerSeries( dml, z ) END
- ELSE
- answer := CplxFn( alpha, beta - 1, z );
- IF beta # 1 THEN answer := answer - (beta - 1) * CplxFn( alpha, beta, z ) END;
- answer := answer / (alpha * z)
- END;
- RETURN answer
- END DCplxFn;
- BEGIN
- tolerance := MathRe.Sqrt( NbrRe.Epsilon )
- END MathMitLef.
|