module Math.Root.Finder.Brent
( Brent
, brent
) where
import Math.Root.Bracket
import Math.Root.Finder
import Data.Maybe
import Text.Printf
data Brent a b = Brent
{ brA :: !a
, brFA :: !b
, brB :: !a
, brFB :: !b
, brC :: !a
, brFC :: !b
, brE :: a
} deriving (Eq, Show)
instance (RealFloat a, Real b, Fractional b) => RootFinder Brent a b where
initRootFinder f x1 x2 = fixMagnitudes (Brent x1 f1 x2 f2 x1 f1 dx)
where f1 = f x1; f2 = f x2; dx = x2 x1
stepRootFinder f r@(Brent a fa b fb c fc e)
| e >= 2 * min tol1 abs_s
&& 1.5 * m * signum s >= tol1 + abs_s
= advance s abs_s
| otherwise = advance m (abs (b a))
where
tol1 = 1e-3 * (abs b + 0.5)
abs_s = abs s
m = 0.5 * (c b)
s | fa /= fc && fa /= fb
= let a' = realToFrac (fa / (fc fb))
b' = realToFrac (fb / (fc fa))
c' = realToFrac (fc / (fb fa))
in (a' * b' * c) ((a' * c' + 1) * b) + (a * b' * c')
| otherwise
= (c b) * realToFrac (fb / (fb fc))
advance d newE = update b' (f b') newE r
where
b' = if abs d > tol1 then b + d else b + tol1 * signum m
estimateRoot = brB
estimateError = brE
converged _ Brent{brFB = 0} = True
converged tol Brent{brB = b, brE = e} =
abs e <= 4 * eps * abs b + tol
defaultNSteps = realFloatDefaultNSteps 5
brent :: RealFloat a => (a -> a) -> a -> a -> a -> Either (Brent a a) a
brent f x1 x2 xacc = fmap estimateRoot (findRoot f x1 x2 xacc)
update :: (Num a, Num b, Ord b) => a -> b -> a -> Brent a b -> Brent a b
update b fb e r@Brent{brB = a, brFB = fa}
= fixMagnitudes (fixSigns r{brA = a, brFA = fa, brB = b, brFB = fb, brE = e})
fixSigns :: (Num a, Num b, Ord b) => Brent a b -> Brent a b
fixSigns br@Brent{ brA = a
, brFA = fa, brFB = fb, brFC = fc }
| (fb > 0 && fc > 0) || (fb < 0 && fc < 0)
= br { brC = a, brFC = fa }
| otherwise
= br
fixMagnitudes :: (Num b, Ord b) => Brent a b -> Brent a b
fixMagnitudes br@Brent{ brC = c, brB = b
, brFC = fc, brFB = fb }
| abs fc < abs fb
= br { brA = b, brFA = fb
, brB = c, brFB = fc
, brC = b, brFC = fb
}
| otherwise
= br
_traceBrent :: (PrintfArg a, RealFloat a,
PrintfArg b, Ord b, Num b,
RootFinder Brent a b) =>
(a -> b) -> Maybe (a, a) -> IO ()
_traceBrent f mbRange = do
xs <- sequence
[ put br >> return br
| br <- traceRoot f x0 x1 (Just eps)
]
putStrLn "(converged)"
go (last xs)
where
(x0,x1) = fromMaybe (last (bracket f 0 1)) mbRange
put Brent{brA=a, brB=b, brC=c, brFA=fa, brFB=fb, brFC=fc} =
putStrLn . map fixPlus $
printf (concat (replicate 6 "%-+25g")) a b c fa fb fc
fixPlus '+' = ' '
fixPlus c = c
go x
| x == x' = return ()
| otherwise = put x >> go x'
where x' = stepRootFinder f x