module MathObj.Gaussian.Polynomial where
import qualified MathObj.Gaussian.Bell as Bell
import qualified MathObj.LaurentPolynomial as LPoly
import qualified MathObj.Polynomial.Core as PolyCore
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.Absolute as Absolute
import qualified Algebra.Ring as Ring
import qualified Algebra.Additive as Additive
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, )
import Control.Monad (liftM2, )
import NumericPrelude.Numeric
import NumericPrelude.Base hiding (reverse, )
data T a = Cons {bell :: Bell.T a, polynomial :: Poly.T (Complex.T a)}
deriving (Show)
instance (Absolute.C a, ZeroTestable.C a, Eq a) => Eq (T a) where
(==) = equal
data RootProduct a = RootProduct a a
instance (Absolute.C a, ZeroTestable.C a, Eq a) => Eq (RootProduct a) where
(RootProduct xr xa) == (RootProduct yr ya) =
let xp = xr*xa^2
yp = yr*ya^2
in xp==yp &&
(isZero xp || signum xa == signum ya)
instance (ZeroTestable.C a) => ZeroTestable.C (RootProduct a) where
isZero (RootProduct r a) = isZero r || isZero a
equal :: (Absolute.C a, ZeroTestable.C a, Eq a) => T a -> T a -> Bool
equal x y =
let bx = bell x
by = bell y
scaleSqr b =
(\p ->
(fmap (RootProduct (Bell.amp b) . Complex.real) p,
fmap (RootProduct (Bell.amp b) . Complex.imag) p))
. polynomial
in Rec.equal
(equating Bell.c0 :
equating Bell.c1 :
equating Bell.c2 :
[])
bx by
&&
scaleSqr bx x == scaleSqr by y
instance (Absolute.C a, ZeroTestable.C a, Arbitrary a) => Arbitrary (T a) where
arbitrary =
liftM2 Cons
arbitrary
(fmap (Poly.fromCoeffs . take 5 . Poly.coeffs) arbitrary)
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}
unit :: (Ring.C a) => T a
unit = eigenfunction0
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, Absolute.C a, ZeroTestable.C a, Eq 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, convolveByFourier :: (Field.C a) =>
T a -> T a -> T a
convolve = convolveByFourier
convolveByFourier 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]) =
PolyCore.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