numeric-prelude 0.4.0.3 → 0.4.1
raw patch · 15 files changed
+216/−50 lines, 15 filesdep ~array
Dependency ranges changed: array
Files
- numeric-prelude.cabal +3/−3
- src/Algebra/Additive.hs +1/−0
- src/Algebra/Algebraic.hs +1/−0
- src/Algebra/Field.hs +1/−0
- src/Algebra/IntegralDomain.hs +1/−0
- src/Algebra/RealRing.hs +1/−0
- src/Algebra/Ring.hs +1/−0
- src/Algebra/Transcendental.hs +3/−1
- src/Algebra/Units.hs +1/−0
- src/MathObj/Polynomial/Core.hs +18/−13
- src/MathObj/PowerSeries/Core.hs +61/−12
- src/MathObj/PowerSeries2.hs +6/−2
- src/MathObj/PowerSeries2/Core.hs +5/−0
- test/Test/MathObj/Polynomial.hs +33/−1
- test/Test/MathObj/PowerSeries.hs +80/−18
numeric-prelude.cabal view
@@ -1,5 +1,5 @@ Name: numeric-prelude-Version: 0.4.0.3+Version: 0.4.1 License: BSD3 License-File: LICENSE Author: Dylan Thurston <dpt@math.harvard.edu>, Henning Thielemann <numericprelude@henning-thielemann.de>, Mikael Johansson@@ -151,7 +151,7 @@ default: False Source-Repository this- Tag: 0.4.0.3+ Tag: 0.4.1 Type: darcs Location: http://code.haskell.org/numeric-prelude/ @@ -170,7 +170,7 @@ -- splitBase Build-Depends:- array >=0.1 && <0.5,+ array >=0.1 && <0.6, containers >=0.1 && <0.6, random >=1.0 && <1.1, base >= 2 && <5
src/Algebra/Additive.hs view
@@ -58,6 +58,7 @@ -} class C a where+ {-# MINIMAL zero, (+), ((-) | negate) #-} -- | zero element of the vector space zero :: a -- | add and subtract elements
src/Algebra/Algebraic.hs view
@@ -21,6 +21,7 @@ {- | Minimal implementation: 'root' or '(^\/)'. -} class (Field.C a) => C a where+ {-# MINIMAL root | (^/) #-} sqrt :: a -> a sqrt = root 2 -- sqrt x = x ** (1/2)
src/Algebra/Field.hs view
@@ -68,6 +68,7 @@ -} class (Ring.C a) => C a where+ {-# MINIMAL recip | (/) #-} (/) :: a -> a -> a recip :: a -> a fromRational' :: Rational -> a
src/Algebra/IntegralDomain.hs view
@@ -95,6 +95,7 @@ Minimal definition: 'divMod' or ('div' and 'mod') -} class (Ring.C a) => C a where+ {-# MINIMAL divMod | (div, mod) #-} div, mod :: a -> a -> a divMod :: a -> a -> (a,a)
src/Algebra/RealRing.hs view
@@ -115,6 +115,7 @@ -} class (Absolute.C a, Ord a) => C a where+ {-# MINIMAL splitFraction | floor #-} splitFraction :: (Ring.C b) => a -> (b,a) fraction :: a -> a ceiling, floor :: (Ring.C b) => a -> b
src/Algebra/Ring.hs view
@@ -65,6 +65,7 @@ -} class (Additive.C a) => C a where+ {-# MINIMAL (*), (one | fromInteger) #-} (*) :: a -> a -> a one :: a fromInteger :: Integer -> a
src/Algebra/Transcendental.hs view
@@ -31,9 +31,10 @@ branch cuts, etc. Minimal complete definition:- pi, exp, log, sin, cos, asin, acos, atan+ pi, exp, (log or logBase), sin, cos, atan -} class (Algebraic.C a) => C a where+ {-# MINIMAL pi, exp, (log | logBase), sin, cos, atan #-} pi :: a exp, log :: a -> a logBase, (**) :: a -> a -> a@@ -56,6 +57,7 @@ x ** y = exp (log x * y) logBase x y = log y / log x+ log = logBase (exp 1) tan x = sin x / cos x
src/Algebra/Units.hs view
@@ -70,6 +70,7 @@ -} class (Integral.C a) => C a where+ {-# MINIMAL isUnit, (stdUnit | stdUnitInv) #-} isUnit :: a -> Bool stdAssociate, stdUnit, stdUnitInv :: a -> a
src/MathObj/Polynomial/Core.hs view
@@ -152,21 +152,26 @@ {- snd $ Poly.divMod (repeat (1::Double)) [1,1] -}+{- |+The modulus will always have one element less than the divisor.+This means that the modulus will be denormalized in some cases,+e.g. @mod [2,1,1] [1,1,1] == [1,0]@ instead of @[1]@.+-} divModRev :: (ZeroTestable.C a, Field.C a) => [a] -> [a] -> ([a], [a]) divModRev x y =- let (y0:ys) = dropWhile isZero y- -- the second parameter represents lazily (length x - length y)- aux xs' =- forcePair .- switchL- ([], xs')- (P.const $- let (x0:xs) = xs'- q0 = x0/y0- in mapFst (q0:) . aux (sub xs (scale q0 ys)))- in if isZero y- then error "MathObj.Polynomial: division by zero"- else aux x (drop (length y - 1) x)+ case dropWhile isZero y of+ [] -> error "MathObj.Polynomial: division by zero"+ y0:ys ->+ let -- the second parameter represents lazily (length x - length (normalize y))+ aux xs' =+ forcePair .+ switchL+ ([], xs')+ (P.const $+ let (x0:xs) = xs'+ q0 = x0/y0+ in mapFst (q0:) . aux (sub xs (scale q0 ys)))+ in aux x (drop (length ys) x) {-# INLINE stdUnit #-} stdUnit :: (ZeroTestable.C a, Ring.C a) => [a] -> a
src/MathObj/PowerSeries/Core.hs view
@@ -76,6 +76,16 @@ zipWith id (cycle [id, P.const zero, NP.negate, P.const zero]) +{- |+For power series of @f x@, compute the power series of @f(x^n)@.+-}+insertHoles :: Additive.C a => Int -> [a] -> [a]+insertHoles n =+ if n<=0+ then error $ "insertHoles requires positive exponent, but got " ++ show n+ else concatMap (\x -> x : replicate (n-1) zero)++ {- * Series arithmetic -} add, sub :: (Additive.C a) => [a] -> [a] -> [a]@@ -159,18 +169,23 @@ {- pow alpha t = t^alpha (pow alpha . x)' = alpha * (pow (alpha-1) . x) * x'-alpha * (pow alpha . x) = x * x' * (pow alpha . x)'+(pow alpha . x)' * x = alpha * (pow alpha . x) * x'+ y = pow alpha . x-alpha * y = x * x' * y'+y' * x = alpha * y * x'++This yields an implementation that is a fused+exp (alpha * log x) -} {- |-Input series must start with non-zero term.+Input series must start with a non-zero term,+even better with a positive one. -} pow :: (Field.C a) => (a -> a) -> a -> [a] -> [a] pow f0 expon x = let y = integrate (f0 (head x)) y'- y' = scale expon (divide y (mul x (differentiate x)))+ y' = scale expon (mul y (derivedLog x)) in y @@ -257,22 +272,56 @@ composeTaylor x [] = x 0 +{-+X(t) = t*x(t)+R(t) = t*r(t) +r(t) = 1 / (x(r(t)*t))+R(t)/t+ = 1 / (x(R(t)))+ = 1 / (X(R(t)) / R(t))+ = 1 / (t / R(t))+-}++{- |+This function returns the series of the inverse function in the form:+(point of the expansion, power series).++That is, say we have the equation:++> y = a + f(x)++where function f is given by a power series with f(0) = 0.+We want to solve for x:++> x = f^-1(y-a)++If you pass the power series of @a+f(x)@ to 'inv',+you get @(a, f^-1)@ as answer, where @f^-1@ is a power series.++The linear term of @f@ (the coefficient of @x@) must be non-zero.++This needs cubic run-time and thus is exceptionally slow.+Computing inverse series for special power series might be faster.+-}+-- how about NonEmpty.T here?+inv :: (Eq a, Field.C a) => [a] -> (a, [a])+inv [] = error "inv: power series must be non-zero"+inv (x:xs) =+ (x, let r = divide [1] (compose xs r) in 0 : r)++ {- (x . y) = id (x' . y) * y' = 1 y' = 1 / (x' . y) -} -{- |-This function returns the series of the function in the form:-(point of the expansion, power series)--This is exceptionally slow and needs cubic run-time.+{-+Like 'inv' but with a slightly cumbersome implementation. -}--inv :: (Field.C a) => [a] -> (a, [a])-inv x =+invDiff :: (Field.C a) => [a] -> (a, [a])+invDiff x = let y' = divide [1] (compose (differentiate x) (tail y)) y = integrate 0 y' -- the first term is zero, which is required for composition
src/MathObj/PowerSeries2.hs view
@@ -86,6 +86,11 @@ const x = lift0 [[x]] +{-# INLINE truncate #-}+truncate :: Int -> T a -> T a+truncate n = lift1 (take n)++ instance Functor T where fmap f (Cons xs) = Cons (map (map f) xs) @@ -124,5 +129,4 @@ instance (Algebraic.C a) => Algebraic.C (T a) where sqrt = lift1 (Core.sqrt Algebraic.sqrt)--- x ^/ y = lift1 (Core.pow (Algebraic.^/ y)--- (fromRational' y)) x+ x ^/ y = lift1 (Core.pow (Algebraic.^/ y) (fromRational' y)) x
src/MathObj/PowerSeries2/Core.hs view
@@ -59,6 +59,11 @@ lift1fromPowerSeries $ PSCore.sqrt (PS.const . (\[x] -> fSqRt x) . PS.coeffs) +pow :: (Field.C a) =>+ (a -> a) -> a -> T a -> T a+pow fPow expon =+ lift1fromPowerSeries $+ PSCore.pow (PS.const . (\[x] -> fPow x) . PS.coeffs) (PS.const expon) swapVariables :: T a -> T a
test/Test/MathObj/Polynomial.hs view
@@ -11,6 +11,7 @@ import qualified Algebra.Laws as Laws import qualified Data.List as List+import Data.Tuple.HT (mapPair, mapSnd, ) import Test.NumericPrelude.Utility (testUnit) import Test.QuickCheck (Property, quickCheck, (==>), Testable, )@@ -30,7 +31,35 @@ mul :: (Ring.C a, Eq a, ZeroTestable.C a) => [a] -> [a] -> Bool mul xs ys = PolyCore.equal (PolyCore.mul xs ys) (PolyCore.mulShear xs ys) +divNormal :: [Rational] -> [Rational] -> Property+divNormal x y =+ case (PolyCore.normalize x, PolyCore.normalize y) of+ (nx, ny) ->+ not (null ny) ==>+ mapSnd PolyCore.normalize (PolyCore.divMod nx ny)+ ==+ mapPair+ (PolyCore.normalize, PolyCore.normalize)+ (PolyCore.divMod x y) +normalizedQuotient :: [Rational] -> [Rational] -> Property+normalizedQuotient x y =+ case PolyCore.normalize x of+ nx ->+ not (isZero y) ==>+ let z = fst $ PolyCore.divMod nx y+ in PolyCore.normalize z == z++modulusSize :: [Rational] -> [Rational] -> Property+modulusSize x y =+ case PolyCore.normalize y of+ ny ->+ not (null ny) ==>+ List.length (snd $ PolyCore.divMod x y)+ <+ List.length ny++ test :: Testable a => (Poly.T Integer -> a) -> IO () test = quickCheck @@ -52,5 +81,8 @@ ("multiplication, commutative", test (Laws.commutative (*))) : ("multiplication, associative", test (Laws.associative (*))) : ("multiplication and addition, distributive", test (Laws.leftDistributive (*) (+))) :- ("division", testRat (Integral.propInverse)) :+ ("division", testRat Integral.propInverse) :+ ("division, normalize", quickCheck divNormal) :+ ("normalized quotient", quickCheck normalizedQuotient) :+ ("modulus size", quickCheck modulusSize) : []
test/Test/MathObj/PowerSeries.hs view
@@ -1,12 +1,13 @@ {-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances #-} module Test.MathObj.PowerSeries where +import qualified MathObj.PowerSeries as PST import qualified MathObj.PowerSeries.Core as PS import qualified MathObj.PowerSeries.Example as PSE -import Test.NumericPrelude.Utility (equalInfLists {- , testUnit -} )+import qualified Test.QuickCheck.Modifiers as Mod+import Test.NumericPrelude.Utility (equalInfLists, testUnit)+import Test.QuickCheck (quickCheck) -- import Test.QuickCheck (Property, quickCheck, (==>)) import qualified Test.HUnit as HUnit @@ -58,6 +59,20 @@ ("sqrt", 500, 1:1:repeat 0, PS.sqrt (\1 -> 1) (PS.mul [1,1] [1,1])) : [] +identitiesHoles :: [(String, Int, [Rational] -> [Rational], Rational)]+identitiesHoles =+ ("exp", 30, PS.exp (\0 -> 1), 0) :+ ("log", 30, PS.log (\1 -> 0), 1) :+ ("tan", 20, PS.tan (\0 -> (0,1)), 0) :+ ("atan", 20, PS.atan (\0 -> 0), 0) :+ ("sin", 20, PS.sin (\0 -> (0,1)), 0) :+ ("cos", 20, PS.cos (\0 -> (0,1)), 0) :+ ("asin", 30, PS.asin (\1 -> 1) (\0 -> 0), 0) :+ ("sqrt", 50, PS.sqrt (\1 -> 1), 1) :+ ("pow13", 30, PS.pow (\1 -> 1) (1/3), 1) :+ ("pow25", 30, PS.pow (\1 -> 1) (2/5), 1) :+ []+ testSeriesIdentity :: (String, Int, [Rational], [Rational]) -> HUnit.Test testSeriesIdentity (label, len, x, y) = HUnit.test (HUnit.assertBool label (equalInfLists len [x,y]))@@ -68,36 +83,83 @@ HUnit.TestLabel label $ HUnit.TestList $ map testSeriesIdentity ids -checkSeriesIdentities ::+_checkSeriesIdentities :: [(String, Int, [Rational], [Rational])] -> [(String,Bool)]-checkSeriesIdentities =+_checkSeriesIdentities = map (\(label, len, x, y) -> (label, equalInfLists len [x,y])) +holesMultiplicative :: Int -> Int -> Int -> [Rational] -> Bool+holesMultiplicative trunc expon0 expon1 xs =+ let n0 = 1 + mod expon0 10+ n1 = 1 + mod expon1 10+ in equalInfLists trunc+ [PS.insertHoles n0 $ PS.insertHoles n1 xs,+ PS.insertHoles n1 $ PS.insertHoles n0 xs,+ PS.insertHoles (n0*n1) xs] +testHolesIdentity ::+ (String, Int, [Rational] -> [Rational], Rational) -> HUnit.Test+testHolesIdentity (label, len, f, x0) =+ HUnit.test $ testUnit $ (,) ("holes in " ++ label) $+ quickCheck $ \expon0 xs -> checkHoles len expon0 f x0 xs -powerMult :: Rational -> Rational -> Bool-powerMult exp0 exp1 =- PS.mul (PSE.pow exp0) (PSE.pow exp1) == PSE.pow (exp0+exp1) -powerExplODE :: Rational -> Bool-powerExplODE expon =- PSE.powODE expon == PSE.powExpl expon+checkHoles ::+ Int -> Int -> ([Rational] -> [Rational]) ->+ Rational -> [Rational] -> Bool+checkHoles trunc expon0 f x xs =+ let expon = 1 + mod expon0 10+ in equalInfLists trunc+ [(f $ PS.insertHoles expon (x:xs)) ++ repeat zero,+ (PS.insertHoles expon $ f $ x:xs) ++ repeat zero] +powerMultSeries :: Int -> Integer -> Mod.Positive Rational -> [Rational] -> Bool+powerMultSeries trunc expon0 xp xs =+ let expon = 1 + mod expon0 10+ x = Mod.getPositive xp+ xt = x:xs+ in equalInfLists trunc+ [PS.pow+ (const x) (1 % expon)+ (PST.coeffs (PST.fromCoeffs xt ^ expon))+ ++ repeat zero,+ xt ++ repeat zero]++powerMult :: Int -> Rational -> Rational -> Bool+powerMult trunc exp0 exp1 =+ equalInfLists trunc+ [PS.mul (PSE.pow exp0) (PSE.pow exp1), PSE.pow (exp0+exp1)]++powerExplODE :: Int -> Rational -> Bool+powerExplODE trunc expon =+ equalInfLists trunc [PSE.powODE expon, PSE.powExpl expon]++invDiff :: Int -> Rational -> Mod.NonZero Rational -> [Rational] -> Bool+invDiff trunc x0 x1 xs_ =+ let xs = x0 : Mod.getNonZero x1 : xs_+ (y,ys) = PS.inv xs+ (z,zs) = PS.invDiff xs+ in y==z && equalInfLists trunc [ys, zs]++ tests :: HUnit.Test tests = HUnit.TestLabel "power series" $ HUnit.TestList [ testSeriesIdentities "explicit vs. ODE solution" identitiesExplODE, testSeriesIdentities "transcendent functions of series" identitiesSeriesFunction,- testSeriesIdentities "inverses of some series" identitiesInverses-{-+ testSeriesIdentities "inverses of some series" identitiesInverses, HUnit.TestLabel "laws" $ HUnit.TestList $- map testUnit $- ("products of powers", quickCheck (powerMult)) :- ("power explicit vs. ODE", quickCheck (powerExplODE)) :- []--}+ map testHolesIdentity identitiesHoles+ +++ (map testUnit $+ ("multiplicative holes", quickCheck (holesMultiplicative 100)) :+ ("powers of series", quickCheck (powerMultSeries 15)) :+ ("products of powers", quickCheck (powerMult 30)) :+ ("power explicit vs. ODE", quickCheck (powerExplODE 50)) :+ ("inv vs. invDiff", quickCheck (invDiff 15)) :+ []) ]