@@ -499,38 +499,25 @@ besselk_power_series(v, x::Float32) = Float32(besselk_power_series(v, Float64(x)
499
499
besselk_power_series (v, x:: ComplexF32 ) = ComplexF32 (besselk_power_series (v, ComplexF64 (x)))
500
500
501
501
function besselk_power_series (v, x:: ComplexOrReal{T} ) where T
502
- MaxIter = 1000
503
- S = eltype (x)
504
- v, x = S (v), S (x)
505
-
506
- z = x / 2
507
- zz = z * z
508
- logz = log (z)
509
- xd2_v = exp (v* logz)
510
- xd2_nv = inv (xd2_v)
511
-
512
- # use the reflection identify to calculate gamma(-v)
513
- # use relation gamma(v)*v = gamma(v+1) to avoid two gamma calls
514
- gam_v = gamma (v)
515
- gam_nv = π / (sinpi (- abs (v)) * gam_v * v)
516
- gam_1mv = - gam_nv * v
517
- gam_1mnv = gam_v * v
518
-
519
- _t1 = gam_v * xd2_nv * gam_1mv
520
- _t2 = gam_nv * xd2_v * gam_1mnv
521
- (xd2_pow, fact_k, out) = (one (S), one (S), zero (S))
522
- for k in 0 : MaxIter
523
- t1 = xd2_pow * T (0.5 )
524
- tmp = muladd (_t1, gam_1mnv, _t2 * gam_1mv)
525
- tmp *= inv (gam_1mv * gam_1mnv * fact_k)
526
- term = t1 * tmp
527
- out += term
528
- abs (term / out) < eps (T) && break
529
- (gam_1mnv, gam_1mv) = (gam_1mnv* (one (S) + v + k), gam_1mv* (one (S) - v + k))
530
- xd2_pow *= zz
531
- fact_k *= k + one (S)
502
+ Math. isnearint (v) && return besselk_power_series_int (v, x)
503
+ MaxIter = 5000
504
+ gam = gamma (v)
505
+ ngam = π / (sinpi (- abs (v)) * gam * v)
506
+
507
+ s1, s2 = zero (T), zero (T)
508
+ t1, t2 = one (T), one (T)
509
+
510
+ for k in 1 : MaxIter
511
+ s1 += t1
512
+ s2 += t2
513
+ t1 *= x^ 2 / (4 k * (k - v))
514
+ t2 *= x^ 2 / (4 k * (k + v))
515
+ abs (t1) < eps (T) && break
532
516
end
533
- return out
517
+
518
+ xpv = (x/ 2 )^ v
519
+ s = gam * s1 + xpv^ 2 * ngam * s2
520
+ return s / (2 * xpv)
534
521
end
535
522
besselk_power_series_cutoff (nu, x:: Float64 ) = x < 2.0 || nu > 1.6 x - 1.0
536
523
besselk_power_series_cutoff (nu, x:: Float32 ) = x < 10.0f0 || nu > 1.65f0 * x - 8.0f0
615
602
end
616
603
)
617
604
end
605
+
606
+ # This is an expansion of the function
607
+ #
608
+ # f_0(v, x) = (x^v)*gamma(-v) + (x^(-v))*gamma(v)
609
+ # = (x^v)*(gamma(-v) + (x^(-2*v))*gamma(v))
610
+ #
611
+ # around v ∼ 0. As you can see by plugging that second form into Wolfram Alpha
612
+ # and getting an expansion back, this is actually a bivariate polynomial in
613
+ # (v^2, log(x)). So that's how this is structured.
614
+ @inline function f0_local_expansion_v0 (v, x)
615
+ lx = log (x)
616
+ c0 = evalpoly (lx, (- 1.1544313298030657 , - 2.0 ))
617
+ c2 = evalpoly (lx, ( 1.4336878944573288 , - 1.978111990655945 , - 0.5772156649015329 , - 0.3333333333333333 ))
618
+ c4 = evalpoly (lx, (- 0.6290784463642211 , - 1.4584260788225176 , - 0.23263776388631713 , - 0.32968533177599085 , - 0.048101305408461074 , - 0.016666666666666666 ))
619
+ evalpoly (v* v, (c0,c2,c4))/ 2
620
+ end
621
+
622
+ # This function assumes |v| < 1e-6 or 1e-7!
623
+ #
624
+ # TODO (cg 2023/05/16 18:07): lots of micro-optimizations.
625
+ function besselk_power_series_temme_basal (v:: V , x:: Float64 ) where {V}
626
+ max_iter = 50
627
+ T = promote_type (V,Float64)
628
+ z = x/ 2
629
+ zz = z* z
630
+ fk = f0_local_expansion_v0 (v, x/ 2 )
631
+ zv = z^ v
632
+ znv = inv (zv)
633
+ gam_1pv = GammaFunctions. gamma_near_1 (1 + v)
634
+ gam_1nv = GammaFunctions. gamma_near_1 (1 - v)
635
+ (pk, qk, _ck, factk, vv) = (znv* gam_1pv/ 2 , zv* gam_1nv/ 2 , one (T), one (T), v* v)
636
+ (out_v, out_vp1) = (zero (T), zero (T))
637
+ for k in 1 : max_iter
638
+ # add to the series:
639
+ ck = _ck/ factk
640
+ term_v = ck* fk
641
+ term_vp1 = ck* (pk - (k- 1 )* fk)
642
+ out_v += term_v
643
+ out_vp1 += term_vp1
644
+ # check for convergence:
645
+ ((abs (term_v) < eps (T)) && (abs (term_vp1) < eps (T))) && break
646
+ # otherwise, increment new quantities:
647
+ fk = (k* fk + pk + qk)/ (k^ 2 - vv)
648
+ pk /= (k- v)
649
+ qk /= (k+ v)
650
+ _ck *= zz
651
+ factk *= k
652
+ end
653
+ (out_v, out_vp1/ z)
654
+ end
655
+
656
+ function besselk_power_series_int (v, x:: Float64 )
657
+ v < zero (v) && return besselk_power_series_int (- v, x)
658
+ flv = Int (floor (v))
659
+ _v = v - flv
660
+ (kv, kvp1) = besselk_power_series_temme_basal (_v, x)
661
+ abs (v) < 1 / 2 && return kv
662
+ twodx = 2 / x
663
+ for _ in 1 : (flv- 1 )
664
+ _v += 1
665
+ (kv, kvp1) = (kvp1, muladd (twodx* _v, kvp1, kv))
666
+ end
667
+ kvp1
668
+ end
669
+
0 commit comments