@@ -446,6 +446,30 @@ function _kronrod_b(J::AbstractSymTri{<:Real}, n::Integer)
446446 return b
447447end
448448
449+ # Laurie's algorithm seems to suffer from spurious underflow,
450+ # in which the s, t factors get exponentially smaller as n
451+ # increases. A workaround is to simply renormalize them on
452+ # each step, which is allowed because our final answer only
453+ # depends on their ratios. There is probably a more elegant
454+ # workaround, and we could be more clever about normalizing
455+ # only where needed, but this only slows down kronrod by
456+ # by a constant factor (about 1%), so not much payoff in being clever.
457+ # (Laurie mentions this problem in passing: they work around it
458+ # by rescaling the interval by 2, which rescales b by 4 and hence
459+ # causes b to asymptote to 1.0 rather than 0.25, suppressing the
460+ # exponential underflow. It's not clear how to generalize that
461+ # hack to arbitrary Jacobi matrices, however.) See issue #93.
462+ function normalize2! (s, t, maxabs, n)
463+ if ! iszero (maxabs)
464+ scaleby = inv (maxabs)
465+ for i = 1 : n
466+ s[i] *= scaleby
467+ t[i] *= scaleby
468+ end
469+ end
470+ return s, t
471+ end
472+
449473# return the Kronrod–Jacobi matrix
450474function _kronrodjacobi (J:: AbstractSymTri{<:Real} , b:: AbstractVector{T} , n:: Int ) where {T<: AbstractFloat }
451475 # these are checked above:
@@ -461,37 +485,39 @@ function _kronrodjacobi(J::AbstractSymTri{<:Real}, b::AbstractVector{T}, n::Int)
461485 t = zeros (T, length (s))
462486 t[2 ] = b[n+ 1 ]
463487 for m = 0 : n- 2
464- u = zero (T)
488+ maxabs = u = zero (T)
465489 for k = div (m+ 1 ,2 ): - 1 : 0
466490 u += b[k + n + 1 ]* s[k+ 1 ] - (m > k ? b[m - k]* s[k+ 2 ] : zero (T))
467491 if J isa SymTridiagonal
468492 u += (a[k+ n+ 2 ] - a[(m- k)+ 1 ]) * t[k+ 2 ]
469493 end
470494 s[k+ 2 ] = u
495+ maxabs = max (maxabs, abs (u))
471496 end
472- s,t = t,s
497+ t, s = normalize2! (s, t, maxabs, div (m + 1 , 2 ) + 2 ) # normalize & swap
473498 end
474499 for j = div (n,2 ): - 1 : 0
475500 s[j+ 2 ] = s[j+ 1 ]
476501 end
477502 for m = n- 1 : 2 n- 3
478- u = zero (T)
503+ maxabs = u = zero (T)
479504 for k = m+ 1 - n: div (m- 1 ,2 )
480505 j = n - (m - k) - 1
481506 u -= b[k + n + 1 ]* s[j+ 2 ] - b[m - k]* s[j+ 3 ]
482507 if J isa SymTridiagonal
483508 u -= (a[k+ n+ 2 ] - a[(m- k)+ 1 ]) * t[j+ 2 ]
484509 end
485510 s[j+ 2 ] = u
511+ maxabs = max (maxabs, abs (u))
486512 end
513+ t, s = normalize2! (s, t, maxabs, length (s)) # normalize & swap
487514 k = div (m+ 1 ,2 )
488515 j = n - (m - k + 2 )
489516 if 2 k != m
490- b[k+ n+ 1 ] = s [j+ 2 ] / s [j+ 3 ]
517+ b[k+ n+ 1 ] = t [j+ 2 ] / t [j+ 3 ]
491518 elseif J isa SymTridiagonal
492- a[k+ n+ 2 ] = a[k+ 1 ] + (s [j+ 2 ] - b[k+ n+ 1 ] * s [j+ 3 ]) / t [j+ 3 ]
519+ a[k+ n+ 2 ] = a[k+ 1 ] + (t [j+ 2 ] - b[k+ n+ 1 ] * t [j+ 3 ]) / s [j+ 3 ]
493520 end
494- s,t = t,s
495521 end
496522 if J isa SymTridiagonal
497523 a[2 n+ 1 ] = a[n] - b[2 n]* s[2 ]/ t[2 ]
0 commit comments