module MathObj.Gaussian.Polynomial where
import qualified MathObj.Gaussian.Bell as Bell
import qualified MathObj.LaurentPolynomial as LPoly
import qualified MathObj.Polynomial as Poly
import qualified Number.Complex as Complex
import qualified Algebra.ZeroTestable as ZeroTestable
import qualified Algebra.Differential as Differential
import qualified Algebra.Transcendental as Trans
import qualified Algebra.Field as Field
import qualified Algebra.Real as Real
import qualified Algebra.Ring as Ring
import qualified Algebra.Additive as Additive
import Algebra.Transcendental (pi, )
import Algebra.Ring ((*), )
import qualified Data.Record.HT as Rec
import qualified Data.List as List
import Data.Function.HT (nest, )
import Data.Eq.HT (equating, )
import Data.List.HT (mapAdjacent, )
import Data.Tuple.HT (forcePair, )
import Test.QuickCheck (Arbitrary, arbitrary, coarbitrary, )
import Control.Monad (liftM2, )
import NumericPrelude
import PreludeBase hiding (reverse, )
data T a = Cons {bell :: Bell.T a, polynomial :: Poly.T (Complex.T a)}
deriving (Show)
instance Real.C a => Eq (T a) where
(==) = equal
equal :: Real.C a => T a -> T a -> Bool
equal x y =
let bx = bell x
by = bell y
csign c =
Complex.real c > 0 ||
(Complex.real c == 0 && Complex.imag c > 0)
scaleSqr b =
map (\c -> (Complex.scale (Bell.amp b) (c^2), csign c)) .
Poly.coeffs . polynomial
in Rec.equal
(equating Bell.c0 :
equating Bell.c1 :
equating Bell.c2 :
[])
bx by
&&
scaleSqr by x == scaleSqr bx y
instance (Real.C a, Arbitrary a) => Arbitrary (T a) where
arbitrary =
liftM2 Cons
arbitrary
(fmap (Poly.fromCoeffs . take 5 . Poly.coeffs) arbitrary)
coarbitrary = undefined
evaluateSqRt :: (Trans.C a) =>
T a -> a -> Complex.T a
evaluateSqRt f x =
Bell.evaluateSqRt (bell f) x *
Poly.evaluate (polynomial f) (Complex.fromReal $ sqrt pi * x)
constant :: (Ring.C a) => T a
constant =
Cons Bell.constant (Poly.const one)
scale :: (Ring.C a) => a -> T a -> T a
scale x f =
f{polynomial = fmap (Complex.scale x) $ polynomial f}
scaleComplex :: (Ring.C a) => Complex.T a -> T a -> T a
scaleComplex x f =
f{polynomial = fmap (x*) $ polynomial f}
eigenfunction :: (Field.C a) => Int -> T a
eigenfunction =
eigenfunctionDifferential
eigenfunction0 :: (Ring.C a) => T a
eigenfunction0 =
Cons Bell.unit (Poly.fromCoeffs [one])
eigenfunction1 :: (Ring.C a) => T a
eigenfunction1 =
Cons Bell.unit (Poly.fromCoeffs [zero, one])
eigenfunction2 :: (Field.C a) => T a
eigenfunction2 =
Cons Bell.unit (Poly.fromCoeffs [(1/4), zero, one])
eigenfunction3 :: (Field.C a) => T a
eigenfunction3 =
Cons Bell.unit (Poly.fromCoeffs [zero, (3/4), zero, one])
eigenfunctionDifferential :: (Field.C a) => Int -> T a
eigenfunctionDifferential n =
(\f -> f{bell = Bell.unit}) $
nest n (scale (1/4) . differentiate) $
Cons (Bell.Cons one zero zero 2) one
eigenfunctionIterative :: (Field.C a, Real.C a) => Int -> T a
eigenfunctionIterative n =
fst . head . dropWhile (uncurry (/=)) . mapAdjacent (,) $
eigenfunctionIteration $
Cons
Bell.unit
(Poly.fromCoeffs $ replicate n zero ++ [one])
eigenfunctionIteration :: (Field.C a) => T a -> [T a]
eigenfunctionIteration =
iterate (\x ->
let y = fourier x
px = polynomial x
py = polynomial y
c = last (Poly.coeffs px) / last (Poly.coeffs py)
in y{polynomial = fmap (0.5*) (px + fmap (c*) py)})
multiply :: (Ring.C a) =>
T a -> T a -> T a
multiply f g =
Cons
(Bell.multiply (bell f) (bell g))
(polynomial f * polynomial g)
convolve :: (Field.C a) =>
T a -> T a -> T a
convolve f g =
reverse $ fourier $ multiply (fourier f) (fourier g)
fourier :: (Field.C a) =>
T a -> T a
fourier f =
foldr
(\c p ->
let q = differentiate p
in q{polynomial =
Poly.const c +
fmap (Complex.scale (1/2) . Complex.quarterLeft) (polynomial q)})
(Cons (Bell.fourier $ bell f) zero) $
Poly.coeffs $ polynomial f
differentiate :: (Ring.C a) => T a -> T a
differentiate f =
f{polynomial =
Differential.differentiate (polynomial f)
Differential.differentiate (Bell.exponentPolynomial (bell f))
* polynomial f}
integrate ::
(Field.C a, ZeroTestable.C a) =>
T a -> (Complex.T a, T a)
integrate f =
let fs = Poly.coeffs $ polynomial f
(ys,~[r]) =
Poly.divModRev
(zipWith ()
(0 : 0 : diffRev ys)
(List.reverse fs))
(List.reverse $ Poly.coeffs $
Differential.differentiate $
Bell.exponentPolynomial $ bell f)
in forcePair $
if null fs
then (zero, f)
else (r, f{polynomial = Poly.fromCoeffs $ List.reverse ys})
diffRev :: Ring.C a => [a] -> [a]
diffRev xs =
zipWith (*) xs
(drop 1 (iterate (subtract 1) (fromIntegral $ length xs)))
translate :: Ring.C a => a -> T a -> T a
translate d =
translateComplex (Complex.fromReal d)
translateComplex :: Ring.C a => Complex.T a -> T a -> T a
translateComplex d f =
Cons
(Bell.translateComplex d $ bell f)
(Poly.translate d $ polynomial f)
modulate :: Ring.C a => a -> T a -> T a
modulate d f =
Cons
(Bell.modulate d $ bell f)
(polynomial f)
turn :: Ring.C a => a -> T a -> T a
turn d f =
Cons
(Bell.turn d $ bell f)
(polynomial f)
reverse :: Additive.C a => T a -> T a
reverse f =
Cons
(Bell.reverse $ bell f)
(Poly.reverse $ polynomial f)
dilate :: Field.C a => a -> T a -> T a
dilate k f =
Cons
(Bell.dilate k $ bell f)
(Poly.dilate (Complex.fromReal k) $ polynomial f)
shrink :: Ring.C a => a -> T a -> T a
shrink k f =
Cons
(Bell.shrink k $ bell f)
(Poly.shrink (Complex.fromReal k) $ polynomial f)
amplify :: Ring.C a => a -> T a -> T a
amplify k f =
Cons
(Bell.amplify k $ bell f)
(polynomial f)
approximateByBells ::
Field.C a =>
a -> T a -> [(Complex.T a, Bell.T a)]
approximateByBells unit f =
let b = bell f
amps =
approximateByBellsAtOnce
unit
(Complex.scale (recip (2 * Bell.c2 b)) (Bell.c1 b))
(recip (2*unit*Bell.c2 b))
(polynomial f)
in zip (LPoly.coeffs amps) $
map
(\d -> Bell.translate d b)
(laurentAbscissas (unit/2) amps)
approximateByBellsAtOnce ::
Field.C a =>
a -> Complex.T a -> a -> Poly.T (Complex.T a) -> LPoly.T (Complex.T a)
approximateByBellsAtOnce unit d s p =
foldr
(\x amps0 ->
let y = fmap (Complex.scale s) amps0
in
LPoly.shift 1 y
LPoly.shift (1) y +
zipWithAbscissas
(\t z -> (Complex.fromReal t d) * z)
(unit/2) amps0 +
LPoly.const x)
(LPoly.fromCoeffs [])
(Poly.coeffs p)
approximateByBellsByTranslation ::
Field.C a =>
a -> Complex.T a -> a -> Poly.T (Complex.T a) -> LPoly.T (Complex.T a)
approximateByBellsByTranslation unit d s p =
foldr
(\x amps0 ->
let y = fmap (Complex.scale s) amps0
in
LPoly.shift 1 y
LPoly.shift (1) y +
zipWithAbscissas Complex.scale (unit/2) amps0 +
LPoly.const x)
(LPoly.fromCoeffs [])
(Poly.coeffs $ Poly.translate d p)
zipWithAbscissas ::
(Ring.C a) =>
(a -> b -> c) -> a -> LPoly.T b -> LPoly.T c
zipWithAbscissas h unit y =
LPoly.fromShiftCoeffs (LPoly.expon y) $
zipWith h
(laurentAbscissas unit y)
(LPoly.coeffs y)
laurentAbscissas :: Ring.C a => a -> LPoly.T c -> [a]
laurentAbscissas unit =
map (\d -> fromIntegral d * unit) .
iterate (1+) . LPoly.expon