ephemeral-0.0.2.0: src/Ephemeral/Hughes.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RebindableSyntax #-}
{-# OPTIONS_GHC -Wno-x-partial #-}
module Ephemeral.Hughes where
import Control.Comonad.Cofree
import Data.Functor.Identity
import NumHask.Prelude
-- Cofree Identity
type Free :: (Type -> Type) -> (Type -> Type)
data Free f a = Pure a | Free (f (Free f a))
-- https://www.reddit.com/r/haskell/comments/mf8588/deriving_via_free_and_cofree/
-- Hopes <~> Free Maybe
type Hopes :: Type -> Type
data Hopes a = Confirmed a | Failed | Possible (Hopes a)
-- type Cofree :: (Type -> Type) -> (Type -> Type)
-- data Cofree f a = a :< f (Cofree f a)
limit :: (Ord a, Subtractive a, Absolute a) => a -> Cofree Identity a -> a
limit eps (x0 :< Identity xs0) = go x0 xs0
where
go x (x' :< Identity xs) = bool (go x' xs) x' (abs (x - x') < eps)
-- limitf :: (Ord a, Num a) => a -> Cofree Identity a -> a
limitf :: (Ord a, Subtractive a, Absolute a) => a -> Cofree Identity a -> a
limitf eps = fix (\rec (x :< Identity xs@(x' :< _)) -> bool (rec xs) x' (abs (x - x') < eps))
approach :: (Ord a, Subtractive a, Absolute a) => a -> (a -> a) -> a
approach eps f = limit eps (coiter (Identity . f) one)
approachf :: (Ord a, Subtractive a, Absolute a) => a -> (a -> a) -> a
approachf eps f = limitf eps (coiter (Identity . f) one)
sqrootStep :: (Additive a, Divisive a) => a -> a -> a
sqrootStep a x = (x + a / x) / two
sqroot'' :: (Ord a, Absolute a, Field a) => a -> a -> a
sqroot'' eps a = approach eps (sqrootStep a)
sqrootf :: (Ord a, Absolute a, Field a) => a -> a -> a
sqrootf eps a = approachf eps (sqrootStep a)
approach' :: (Ord a, Absolute a, Field a) => a -> (a -> a) -> a
approach' eps f = limit_ eps (iterate f one)
sqroot' :: (Ord a, Absolute a, Field a) => a -> a -> a
sqroot' eps a = approach' eps (sqrootStep a)
limit_ :: (Ord a, Absolute a, Field a) => a -> [a] -> a
limit_ _ [] = error ("empty" :: String)
limit_ eps (x0 : xs0) = go x0 xs0
where
go x [] = x
go x (x' : xs) = bool (go x' xs) x' (abs (x - x') < eps)
limitm :: (Ord a, Absolute a, Field a) => a -> [a] -> a
limitm _ [] = error ("empty" :: String)
limitm eps (x0 : xs0) = go x0 xs0
where
go x [] = x
go x (x' : xs) = bool (go x' xs) x' (abs (x / x' - one) < eps)
-- | sqroot limit_ test
--
-- >>> sqroot 0.0001 2
-- 1.4142135623746899
sqroot :: (Ord a, Absolute a, Field a) => a -> a -> a
sqroot eps a = limit_ eps (iterate next one)
where
next x = (x + a / x) / two
-- | Find the derivative of a function at a point.
--
-- >>> deriv 0.00001 (\x -> x * x) 3
-- 6.000007629394531
--
-- >>> deriv 0.00001 (sqroot 0.00001) 2
-- 0.35354799598098907
deriv :: (Ord a, Absolute a, Field a) => a -> (a -> a) -> a -> a
deriv eps f x =
limit_ eps (map slope (iterate (/ two) one))
where
slope h = (f (x + h) - f x) / h
-- | Integrgrate a function over a range.
--
-- >>> integrate 0.00001 (sqroot 0.00001) 1 2
-- 1.218945096090038
-- >>> f x = x ** 1.5 / 1.5
-- >>> f 2 - f 1
-- 1.2189514164974602
integrate :: (Ord a, Subtractive a, Absolute a, QuotientField a, Whole a ~ Int) => a -> (a -> a) -> a -> a -> a
integrate eps f a b =
limit_ eps (map area (iterate (/ two) one))
where
area h = sum $ (h *) . f <$> take (floor $ (b - a) / h) (iterate (+ h) a)
integrate' :: (Subtractive a, Absolute a, QuotientField a, Whole a ~ Int) => (a -> a) -> a -> a -> [a]
integrate' f a b =
map area (iterate (/ two) one)
where
area h = sum $ (h *) . f <$> take (floor $ (b - a) / h) (iterate (+ h) a)
-- | Hughes' improve idea
--
-- A + B * h^n
-- A + B * (h/2)^n
improve :: (Absolute a, Field a) => Int -> [a] -> [a]
improve _ [] = []
improve _ [x] = [x]
improve n (a : b : rest) = (b * two ^ n - a) / (two ^ n - one) : improve n (b : rest)
-- improveall :: (Fractional a, Num t) => t -> (Int -> [a] -> [a]) -> [Int -> [a] -> [a]]
improveall :: (Absolute a, Field a) => Int -> [a] -> [[a]]
improveall n s = s : improveall (n + 1) (improve n s)
-- >>> limit_ 0.00000001 $ super (integrate' sin 0 4)
-- 1.6536436208622662
super :: (Absolute a, Field a) => [a] -> [a]
super s = map head (improveall 1 s)
-- | Newton-Raphson method for finding roots.
newtonRaphson :: Double -> (Double -> Double) -> Double
newtonRaphson guess f
| difference <= epsilon = newguess
| otherwise = newtonRaphson newguess f
where
newguess = guess - f guess / fprime guess
difference = abs (newguess - guess)
fprime = derivative f
newtonRaphson2 :: Double -> (Double -> Double) -> (Double -> Double) -> Double
newtonRaphson2 guess f fprime
| difference <= epsilon = newguess
| otherwise = newtonRaphson2 newguess f fprime
where
newguess = guess - f guess / fprime guess
difference = abs (newguess - guess)
-- | Newton's method for finding optimization of functions.
optimize :: (Double -> Double) -> Double -> Double
optimize f guess = newtonRaphson guess g
where
g x = derivative2 f x / derivSecond f x
-- | Numerical method for finding the square root.
mysqrt :: Double -> Double -> Double
mysqrt a x
| difference <= epsilon = newguess
| otherwise = mysqrt a newguess
where
newguess = (1 / 2) * (x + a / x)
difference = abs (newguess - x)
-- | returns an approximation of the derivative
-- using forward differences.
derivative :: (Double -> Double) -> Double -> Double
derivative f x = (f (x + epsilon) - f x) / epsilon
-- | returns an approximation of the derivative using the symmetric
-- difference quotient.
derivative2 :: (Double -> Double) -> Double -> Double
derivative2 f x = (f (x + epsilon) - f (x - epsilon)) / (2 * epsilon)
-- finite central differences.
derivative3 :: (Double -> Double) -> Double -> Double
derivative3 f x = (d - 8 * c + 8 * b - a) / (12 * epsilon)
where
a = f (x + 2 * epsilon)
b = f (x + epsilon)
c = f (x - epsilon)
d = f (x - 2 * epsilon)
-- | returns the second derivative of a function.
derivSecond :: (Double -> Double) -> Double -> Double
derivSecond f x = (f (x + epsilon) - 2 * f x + f (x - epsilon)) / epsilon ** 2
deriv' :: (Double -> Double) -> Double -> Double
deriv' f x = (f (x + dx) - f x) / dx
where
dx = epsilon * x
-- root finding
-- f (root f) = 0
-- inverse f y = root (\x -> f x - y)
-- extremum f = root (derivative f)
-- https://hackage.haskell.org/package/math-functions-0.3.4.2/docs/Numeric-RootFinding.html#g:3
-- bisection
-- f a * f b < 0
bisection ::
(Absolute a, Field a, Ord a, Epsilon a, Field b, Ord b) =>
(a -> b) ->
a ->
a ->
a
bisection f a b = let av = (b + a) / two in bool (bool (bisection f av b) (bisection f a av) (f a * f av < zero)) av (b - a < epsilon)
newton :: (Epsilon a, Absolute a, Field a, Ord a) => (a -> a) -> (a -> a) -> a -> a
newton f f' guess =
let newGuess = guess - (f guess / f' guess)
err = abs (newGuess - guess)
in bool (newton f f' newGuess) newGuess (err < epsilon)
towardRoot :: (Field a) => (a -> a) -> (a -> a) -> a -> a
towardRoot f f' x = x - f x / f' x
secant :: (Epsilon a, Absolute a, Field a, Ord a) => (a -> a) -> a -> a -> a
secant f guess1 guess0 =
let newGuess = guess1 - f guess1 * (guess1 - guess0) / (f guess1 - f guess0)
err = abs (newGuess - guess1)
in if err < epsilon
then newGuess
else secant f newGuess guess1