packages feed

gruff-0.1: Nucleus.hs

{-# LANGUAGE Rank2Types #-}
module Nucleus (refineNucleus) where

import Prelude hiding (zipWith)
import Data.Functor ((<$>))
import Data.List (genericIndex, genericSplitAt)
import Data.Vec ((:.)((:.)), toList, solve, fromList, matFromLists, Vec2, NearZero(nearZero), zipWith)
import Text.FShow.Raw (DecimalFormat(nanTest))
import Numeric.AD (jacobian', FF, lift, UU, findZero)
import Fractal.RUFF.Types.Complex (Complex((:+)), cis, magnitude)
import Utils ()

refineNucleus :: (DecimalFormat r, Floating r, Fractional r, Ord r, NearZero r) => Integer -> Complex r -> (r, Complex r)
refineNucleus p g =
  let c = converge $ findZero (nucleusIter' p) g
      [_, bond0] = root2 (bondIter' p (cis ( pi / 3))) [c, c]
      [_, bond1] = root2 (bondIter' p (cis (-pi / 3))) [c, c]
      r = magnitude (bond1 - bond0)
  in  (r, c)

converge :: (DecimalFormat r, NearZero r, Num r) => [Complex r] -> Complex r
converge (x:ys@(y@(yr:+yi):_))
  | nanTest yr || nanTest yi = x
  | nearZero (x - y) = x
  | otherwise = converge ys
converge [x] = x
converge [] = error "gruff.Nucleus.converge: internal error"

-- finding nucleus
nucleusIter' :: Fractional r => Integer -> UU r
nucleusIter' n c = (`genericIndex` n) . iterate (\z -> z * z + c) $ 0

-- finding bond points
fdf :: (Integral i, Num c) => i -> c -> c -> (c, c)
fdf n z c = let (fzs, fz:_) = genericSplitAt n $ iterate (\w -> w * w + c) z
            in  (fz, 2 ^ n * product fzs)

bondIter' :: (Fractional c) => Integer -> c -> FF [] [] c
bondIter' n b [z, c] =
  let b' = lift b
      (fz, dfz) = fdf n z c
      p =  fz - z
      q = dfz - b'
  in  [p, q]
bondIter' _ _ _ = error "bondIter' internal error"

-- Newton's method
root2' :: (Fractional r, Ord r, NearZero r) => FF [] [] r -> Vec2 r -> Vec2 r
root2' f x = go x
  where
    go x0 = 
      let (ys, js) = unzip $ jacobian' f (toList x0)
          y = fromList (negate <$> ys) `asTypeOf` x
          j = matFromLists js `asTypeOf` ((x:.x:.()))
          mdx = solve j y
      in  if all nearZero ys
            then  x0
            else case mdx of
              Nothing ->  x0
              Just dx ->
                let x1 = zipWith (+) x0 dx
                in  if all nearZero (toList dx)
                      then x0
                      else go x1

root2 :: (Fractional r, NearZero r, Ord r) => FF [] [] r -> [r] -> [r]
root2 f = toList . root2' f . fromList