----------------------------------------------------------------------------- -- | -- Module : Chapter04 -- Description : Describing Motion -- -- Copyright : (c) Eric Zoerner, 2023 -- License : BSD3 -- Maintainer : eric.zoerner@proton.me -- ----------------------------------------------------------------------------- module Chapter04 where -- | alias R to be a Double as an approximate of a real number type R = Double -- | A derivative is a function of a real to real that yields another -- function of real to real type Derivative = (R -> R) -> R -> R -- | Numerical derivation given a small delta derivative :: R -> Derivative derivative :: R -> Derivative derivative R dt R -> R x R t = (R -> R x (R t forall a. Num a => a -> a -> a + R dt forall a. Fractional a => a -> a -> a / R 2) forall a. Num a => a -> a -> a - R -> R x (R t forall a. Num a => a -> a -> a - R dt forall a. Fractional a => a -> a -> a / R 2)) forall a. Fractional a => a -> a -> a / R dt ------------------ -- * Exercise 4.1 ------------------ -- $ex41 -- __Why does @derivative 0.1@ not produce exactly the identity function on real numbers?__ -- -- /Because @0.1@ cannot be represented exactly in a Double./ -- -- /See:/ [Why 0.1 Does Not Exist In Floating-Point](https://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point) -- -- >>> derivative 10 f41 1 -- 1.0 -- >>> derivative 1 f41 1 -- 1.0 -- >>> derivative 0.1 f41 1 -- 1.0000000000000002 -- | @f(x) = ½x²@ f41 :: R -> R f41 :: R -> R f41 R x = R 1 forall a. Fractional a => a -> a -> a / R 2 forall a. Num a => a -> a -> a * R x forall a. Floating a => a -> a -> a ** R 2 ------------------ -- * Exercise 4.2 ------------------ {- $ex42 /(See functions 'f42', 'err42' below.)/ /Evaluating the derivative at different values of @x@ (the second argument):/ >>> err42 1 6 0.25 >>> err42 1 0.5 0.25 >>> err42 1 1 0.25 /It appears empirically that the error does not depend on @x@ at all,/ /it only depends on @a@./ /Exploring the error for different values of @a@:/ >>> err42 1 1 0.25 >>> err42 0.1 1 2.500000000002167e-3 >>> err42 0.01 5 2.4999998515795596e-5 >>> err42 0.02 5 9.999999903698154e-5 >>> err42 0.03 5 2.249999984940132e-4 /After some trial and error, it is deduced that it follows a pattern like:/ > err(a) = a²/4 /This formula is the implementation of the 'errA' function below./ __When @x = 4@, @Df (4) = 48@. What value of @a@ produces an error of__ __@1@ percent at @x = 4@?__ /Use the formula we came up with, and solving for @a@:/ @ a²/4 = (0.01)(48) a² = (0.01)(48)(4) a = √ ((0.01)(48)(4)) @ >>> sqrt $ 0.01 * 48 * 4 1.3856406460551018 __When @x = 0.1@, @Df (0.1) = 0.03@.__ __What value of a produces an error of @1@ percent at @x = 0.1@?__ /Again, solving for @a@:/ @ a²/4 = (0.01)(0.03) a² = (0.01)(0.03)(4) a = √ ((0.01)(0.03)(4)) @ >>> sqrt $ 0.01 * 0.03 * 4 3.4641016151377546e-2 -} -- | @f(x) = x³@ f42 :: R -> R f42 :: R -> R f42 R x = R x forall a. Floating a => a -> a -> a ** R 3 -- | Derivative of 'f42', @f(x) = 3x²@. df42 :: R -> R df42 :: R -> R df42 R x = R 3 forall a. Num a => a -> a -> a * R x forall a. Floating a => a -> a -> a ** R 2 -- | Error function: Returns the error between a numerical derivative estimate -- and the provided analytical derivative. errF :: -- | @f@: The original function (R -> R) -> -- | @df@: The analytical derivative function (R -> R) -> -- | @a@: The delta ("dt") value R -> -- | @x@: The x value of the function R -> -- | Returns the error R errF :: (R -> R) -> (R -> R) -> R -> R -> R errF R -> R f R -> R df R a R x = forall a. Num a => a -> a abs forall a b. (a -> b) -> a -> b $ R -> Derivative derivative R a R -> R f R x forall a. Num a => a -> a -> a - R -> R df R x -- | Error function for 'f42'. err42 :: -- | @a@: The delta ("dt") value R -> -- | @x@: The x value of the function R -> -- | Returns the error R err42 :: R -> R -> R err42 = (R -> R) -> (R -> R) -> R -> R -> R errF R -> R f42 R -> R df42 -- | Returns the error in terms of @a@ (the "delta"). -- The expression for the error is apparantly @a²/4@. -- This formula was just determined empirically by putting a bunch of -- @a@ values into 'err42' and looking at the pattern of results. errA :: -- | @a@: The delta value R -> -- | Returns the error R errA :: R -> R errA R a = R a forall a. Floating a => a -> a -> a ** R 2 forall a. Fractional a => a -> a -> a / R 4 ------------------ -- * Exercise 4.3 ------------------ {- $ex43 /Using the expression @'errA' a@ from Exercise 4.2,/ /using @0.01@ for the value of @a@, solve for x in @errA 0.01 >= 0.10 * x@./ @ 0.01²/4 ≥ 0.10x 0.10x ≤ 0.01²/4 x ≤ 0.01²\/4\/0.10 @ >>> 0.01 ** 2 / 4 / 0.10 2.5e-4 /Therefore:/ @x ≤ 2.5e-4@ -} ------------------ -- * Exercise 4.4 ------------------ {- $ex44 -- /TO DO/ -} f44 :: R -> R f44 :: R -> R f44 = forall a. Floating a => a -> a cos df44 :: R -> R df44 :: R -> R df44 R x = -forall a. Floating a => a -> a sin R x ------------------ -- * Exercise 4.5 ------------------ {- $ex45 -- /TO DO/ -} -- Note: analytic derivative of `5x²` is `10x`.