{-# LANGUAGE RebindableSyntax #-}
{- |
Lazy evaluation allows for the solution
 of differential equations in terms of power series.
Whenever you can express the highest derivative of the solution
 as explicit expression of the lower derivatives
 where each coefficient of the solution series
 depends only on lower coefficients,
 the recursive algorithm will work.
-}

module MathObj.PowerSeries.DifferentialEquation where

import qualified MathObj.PowerSeries.Core    as PS
import qualified MathObj.PowerSeries.Example as PSE

import qualified Algebra.Field        as Field
import qualified Algebra.ZeroTestable as ZeroTestable

import NumericPrelude.Numeric
import NumericPrelude.Base


{- |
Example for a linear equation:
   Setup a differential equation for @y@ with

>    y   t = (exp (-t)) * (sin t)
>    y'  t = -(exp (-t)) * (sin t) + (exp (-t)) * (cos t)
>    y'' t = -2 * (exp (-t)) * (cos t)

Thus the differential equation

>    y'' = -2 * (y' + y)

holds.

The following function generates
a power series for @exp (-t) * sin t@
by solving the differential equation.
-}

solveDiffEq0 :: (Field.C a) => [a]
solveDiffEq0 :: [a]
solveDiffEq0 =
   let -- the initial conditions are passed to "PS.integrate"
       y :: [a]
y   = a -> [a] -> [a]
forall a. C a => a -> [a] -> [a]
PS.integrate a
0 [a]
y'
       y' :: [a]
y'  = a -> [a] -> [a]
forall a. C a => a -> [a] -> [a]
PS.integrate a
1 [a]
y''
       y'' :: [a]
y'' = a -> [a] -> [a]
forall a. C a => a -> [a] -> [a]
PS.scale (-a
2) ([a] -> [a] -> [a]
forall a. C a => [a] -> [a] -> [a]
PS.add [a]
y' [a]
y)
   in  [a]
y

verifyDiffEq0 :: (Field.C a) => [a]
verifyDiffEq0 :: [a]
verifyDiffEq0 =
   [a] -> [a] -> [a]
forall a. C a => [a] -> [a] -> [a]
PS.mul ((a -> a -> a) -> [a] -> [a] -> [a]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith a -> a -> a
forall a. C a => a -> a -> a
(*) ((a -> a) -> a -> [a]
forall a. (a -> a) -> a -> [a]
iterate a -> a
forall a. C a => a -> a
negate a
1) [a]
forall a. C a => [a]
PSE.exp) [a]
forall a. C a => [a]
PSE.sin

propDiffEq0 :: Bool
propDiffEq0 :: Bool
propDiffEq0 =  [Rational]
forall a. C a => [a]
solveDiffEq0 [Rational] -> [Rational] -> Bool
forall a. Eq a => a -> a -> Bool
== ([Rational]
forall a. C a => [a]
verifyDiffEq0 :: [Rational])


{- |
We are not restricted to linear equations!
 Let the solution be y with
  y   t =   (1-t)^-1
  y'  t =   (1-t)^-2
  y'' t = 2*(1-t)^-3
 then it holds
  y'' = 2 * y' * y
-}

solveDiffEq1 :: (ZeroTestable.C a, Field.C a) => [a]
solveDiffEq1 :: [a]
solveDiffEq1 =
   let -- the initial conditions are passed to "PS.integrate"
       y :: [a]
y   = a -> [a] -> [a]
forall a. C a => a -> [a] -> [a]
PS.integrate a
1 [a]
y'
       y' :: [a]
y'  = a -> [a] -> [a]
forall a. C a => a -> [a] -> [a]
PS.integrate a
1 [a]
y''
       y'' :: [a]
y'' = a -> [a] -> [a]
forall a. C a => a -> [a] -> [a]
PS.scale a
2 ([a] -> [a] -> [a]
forall a. C a => [a] -> [a] -> [a]
PS.mul [a]
y' [a]
y)
   in  [a]
y

verifyDiffEq1 :: (ZeroTestable.C a, Field.C a) => [a]
verifyDiffEq1 :: [a]
verifyDiffEq1 = [a] -> [a] -> [a]
forall a. C a => [a] -> [a] -> [a]
PS.divide [a
1] [a
1, -a
1]

propDiffEq1 :: Bool
propDiffEq1 :: Bool
propDiffEq1 =  [Rational]
forall a. (C a, C a) => [a]
solveDiffEq1 [Rational] -> [Rational] -> Bool
forall a. Eq a => a -> a -> Bool
== ([Rational]
forall a. (C a, C a) => [a]
verifyDiffEq1 :: [Rational])