packages feed

np-extras (empty) → 0.1

raw patch · 6 files changed

+540/−0 lines, 6 filesdep +basedep +containersdep +numeric-preludesetup-changed

Dependencies added: base, containers, numeric-prelude, primes

Files

+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) Brent Yorgey 2009++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of other contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ MathObj/FactoredRational.hs view
@@ -0,0 +1,196 @@+-- | A representation of rational numbers as lists of prime powers,+--   allowing efficient representation, multiplication and division of+--   large numbers, especially of the sort occurring in combinatorial+--   computations.+-- +--   The module also includes a method for generating factorials in+--   factored form directly, and for computing Euler's totient and+--   generating all divisors of factored integers.+module MathObj.FactoredRational +    ( -- * Type+      T++      -- * Utilities+    , factorial+    , eulerPhi+    , divisors+ +    ) where++import qualified Algebra.Additive as Additive+import qualified Algebra.Ring as Ring+import qualified Algebra.Field as Field+import qualified Algebra.IntegralDomain as Integral++import qualified Algebra.ZeroTestable as ZeroTestable+import qualified Algebra.Real as Real+import qualified Algebra.ToRational as ToRational+import qualified Algebra.RealIntegral as RealIntegral+import qualified Algebra.ToInteger as ToInteger++import Data.Numbers.Primes++import PreludeBase +import NumericPrelude++-- Represent rational numbers by their prime factorizations.+-- Perhaps this should use a sparse representation instead, using a Map from +-- primes to powers?  Well, that should be easy enough to change later.++-- | The type of factored rationals.+--+--   Instances are provided for Eq, Ord, Additive, Ring, ZeroTestable,+--   Real, ToRational, Integral, RealIntegral, ToInteger, and Field.+--+--   Note that currently, addition is performed on factored rationals+--   by converting them to normal rationals, performing the addition,+--   and factoring.  This could probably be made more efficient by+--   finding a common denominator, pulling out common factors from the+--   numerators, and performing the addition and factoring only on the+--   relatively prime parts.+data T = FQZero            -- ^ zero+       | FQ Bool [Integer] -- ^ prime exponents with sign bit, True = negative.++-- XXX this ought to be improved.+instance Show T where+  show FQZero = "0"+  show (FQ True pows) = "(-1)" ++ showPows pows+  show (FQ False pows) = showPows pows++showPows :: [Integer] -> String+showPows pows = concat $ zipWith showPow primes pows+  where showPow p 0 = ""+        showPow p 1 = "(" ++ show p ++ ")"+        showPow p n = "(" ++ show p ++ "^" ++ show n ++ ")"++instance Additive.C T where+  zero = FQZero+  FQZero + a = a+  a + FQZero = a+  x + y = fromRational' (toRational x + toRational y) ++  negate FQZero   = FQZero+  negate (FQ s e) = FQ (not s) e++instance Ring.C T where+  FQZero * _ = FQZero+  _ * FQZero = FQZero+  (FQ s1 e1) * (FQ s2 e2) = FQ (s1 /= s2) (zipWithExt 0 0 (+) e1 e2)++  fromInteger 0 = FQZero+  fromInteger n | n < 0     = FQ True (factor (negate n))+                | otherwise = FQ False (factor n)++  _ ^ 0 = one+  FQZero   ^ _ = FQZero+  (FQ s e) ^ n = FQ s (map (*n) e)++-- | Zip two lists together with a combining function, using default+--   values to extend the lists if one is shorter than the other.+zipWithExt :: a -> b -> (a -> b -> c) -> [a] -> [b] -> [c]+zipWithExt da db f = zipWithExt'+  where zipWithExt' []     bs     = zipWith f (repeat da) bs+        zipWithExt' as     []     = zipWith f as (repeat db)+        zipWithExt' (a:as) (b:bs) = f a b : zipWithExt' as bs++-- | A simple factoring method. +--+--   We should probably just depend on another module with some+--   dedicated, efficient factoring code written by someone really+--   smart, but this simple method works OK for now.+--+--   Precondition: argument is positive.+factor :: Integer -> [Integer]+factor n = factor' n primes+  where+    factor' 1 _ = []+    factor' n (p:ps) = let (k,n') = logRem n p+                       in  k : factor' n' ps+   +-- | @logRem n p@ computes (k,n'), where k is the highest power of p+--   that divides n, and n' = n `div` p^k.+logRem :: Integer -> Integer -> (Integer, Integer)+logRem = logRem' 0+  where logRem' k n p | n `mod` p == 0 = logRem' (k+1) (n `div` p) p+                      | otherwise = (k,n)++instance ZeroTestable.C T where +  isZero FQZero = True+  isZero _      = False++instance Eq T where+  FQZero == FQZero   = True+  FQZero == (FQ _ _) = False+  (FQ _ _) == FQZero = False+  (FQ s1 e1) == (FQ s2 e2) = s1 == s2 && dropZeros e1 == dropZeros e2+    where dropZeros = reverse . dropWhile (==0) . reverse++instance Ord T where+  compare FQZero FQZero = EQ+  compare FQZero (FQ False _) = LT+  compare FQZero (FQ True  _) = GT+  compare (FQ False _) FQZero = GT+  compare (FQ True  _) FQZero = LT+  compare (FQ False _) (FQ True  _)   = GT+  compare (FQ True  _) (FQ False _)   = LT+  compare fq1 fq2 = compare (toRational fq1) (toRational fq2)++instance Real.C T where+  abs FQZero   = FQZero+  abs (FQ _ e) = FQ False e++instance ToRational.C T where+  toRational FQZero   = 0+  toRational (FQ s e) = (if s then negate else id)+                      . product+                      . zipWith (^-) (map (%1) primes)+                      $ e++instance Integral.C T where+  divMod a b =+    if isZero b+      then (undefined,a)+      else (a/b,0)++instance RealIntegral.C T +  -- default definition is fine++instance ToInteger.C T where+  toInteger FQZero = 0+  toInteger (FQ s e) | any (<0) e = error "non-integer in FactoredRational.toInteger"+                     | otherwise  = (if s then negate else id)+                                  . product+                                  . zipWith (^) primes+                                  $ e++instance Field.C T where+  recip FQZero = error "division by zero"+  recip (FQ s e) = FQ s (map negate e)++-- | Efficiently compute n! directly as a factored rational.+factorial :: Integer -> T+factorial 0 = one+factorial 1 = one+factorial n = FQ False (takeWhile (>0) . map (factorialFactors n) $ primes)++-- | @factorialFactors n p@ computes the power of prime p in the+--   factorization of n!.+factorialFactors :: Integer -> Integer -> Integer+factorialFactors n p = sum +                     . takeWhile (>0)+                     . map (n `div`)+                     $ iterate (*p) p++-- | Compute Euler's totient function (@eulerPhi n@ is the number of+--   integers less than and relatively prime to n).  Only makes sense+--   for (nonnegative) integers.+eulerPhi :: T -> Integer+eulerPhi FQZero = 1+eulerPhi (FQ _ pows) = product $ zipWith phiP primes pows+  where phiP _ 0 = 1+        phiP p a = p^(a-1) * (p-1)++-- | List of the divisors of n.  Only makes sense for integers.+divisors :: T -> [T]+divisors FQZero = [1]+divisors (FQ b pows) = map (FQ b) $ mapM (enumFromTo 0) pows
+ MathObj/Monomial.hs view
@@ -0,0 +1,137 @@+{-# LANGUAGE PatternGuards #-}++-- | Monomials in a countably infinite set of variables x1, x2, x3, ...+module MathObj.Monomial+    ( -- * Type+      T(..)++      -- * Creating monomials+    , constant+    , x++      -- * Utility functions+    , degree+    , pDegree+    , scaleMon++    ) where++import qualified Algebra.Additive as Additive+import qualified Algebra.Ring as Ring+import qualified Algebra.ZeroTestable as ZeroTestable+import qualified Algebra.Differential as Differential+import qualified Algebra.Field as Field++import qualified Data.Map as M+import Data.Ord (comparing)+import Control.Arrow ((***))+import Data.List (sort, intercalate)++import NumericPrelude+import PreludeBase++-- | A monomial is a map from variable indices to integer powers,+--   paired with a (polymorphic) coefficient.  Note that negative+--   integer powers are handled just fine, so monomials form a field.+--+--   Instances are provided for Eq, Ord, ZeroTestable, Additive, Ring,+--   Differential, and Field.  Note that adding two monomials only+--   makes sense if they have matching variables and exponents.  The+--   Differential instance represents partial differentiation with+--   respect to x1.+--+--   The Ord instance for monomials orders them first by permutation+--   degree, then by largest variable index (largest first), then by+--   exponent (largest first).  This may seem a bit odd, but in fact+--   reflects the use of these monomials to implement cycle index+--   series, where this ordering corresponds nicely to generation+--   of integer partitions. To make the library more general we could+--   parameterize monomials by the desired ordering.+data T a = Cons { coeff  :: a +                , powers :: M.Map Integer Integer+                }++instance (ZeroTestable.C a, Ring.C a, Eq a, Show a) => Show (T a) where+  show (Cons a pows) | isZero a  = "0"+                     | a == 1    = showVars pows+                     | a == (-1) = "-" ++ showVars pows+                     | otherwise = show a ++ " " ++ showVars pows++showVars :: M.Map Integer Integer -> String+showVars m = intercalate " " $ concatMap showVar (M.toList m)+  where showVar (_,0) = []+        showVar (v,1) = ["x" ++ show v]+        showVar (v,p) = ["x" ++ show v ++ "^" ++ show p]++-- | The degree of a monomial is the sum of its exponents.+degree :: T a -> Integer+degree (Cons _ m) = M.fold (+) 0 m++-- | The \"partition degree\" of a monomial is the sum of the products+--   of each variable index with its exponent.  For example, x1^3 x2^2+--   x4^3 has partition degree 1*3 + 2*2 + 4*3 = 19.  The terminology+--   comes from the fact that, for example, we can view x1^3 x2^2 x4^3+--   as corresponding to an integer partition of 19 (namely, 1 + 1 + 1+--   + 2 + 2 + 4 + 4 + 4).+pDegree :: T a -> Integer+pDegree (Cons _ m) = sum . map (uncurry (*)) . M.assocs $ m++-- | Create a constant monomial.+constant :: a -> T a+constant a = Cons a M.empty++-- | Create the monomial xn for a given n.+x :: (Ring.C a) => Integer -> T a+x n = Cons Ring.one (M.singleton n 1)++-- | Scale all the variable subscripts by a constant.  Useful for+--   operations like plethyistic substitution or Mobius inversion.+scaleMon :: Integer -> T a -> T a+scaleMon n (Cons a m) = Cons a (M.mapKeys (n*) m)++instance Eq (T a) where+  (Cons _ m1) == (Cons _ m2) = m1 == m2++instance Ord (T a) where+  compare m1 m2+    | d1 < d2   = LT+    | d1 > d2   = GT+    | otherwise = comparing q m1 m2+    where d1 = pDegree m1+          d2 = pDegree m2+          q  = map Rev . reverse . sort . M.assocs . powers++newtype Rev a = Rev { getRev :: a }+  deriving Eq+instance Ord a => Ord (Rev a) where+  compare (Rev a) (Rev b) = compare b a++instance (ZeroTestable.C a) => ZeroTestable.C (T a) where+  isZero (Cons a _) = isZero a+  +instance (Additive.C a, ZeroTestable.C a) => Additive.C (T a) where+  zero = Cons zero M.empty+  negate (Cons a m) = Cons (negate a) m++  -- precondition: m1 == m2+  (Cons a1 m1) + (Cons a2 _m2) | isZero s  = Cons s M.empty+                               | otherwise = Cons s m1+                               where s = a1 + a2++instance (Ring.C a, ZeroTestable.C a) => Ring.C (T a) where+  fromInteger n = Cons (fromInteger n) M.empty+  (Cons a1 m1) * (Cons a2 m2) = Cons (a1*a2) +                                     (M.filterWithKey (\_ p -> not (isZero p)) $+                                        M.unionWith (+) m1 m2+                                     )++-- Partial differentiation with respect to x1.+instance (ZeroTestable.C a, Ring.C a) => Differential.C (T a) where+  differentiate (Cons a m) +    | Just 1 <- M.lookup 1 m = Cons a M.empty+    | Just p <- M.lookup 1 m = Cons (a*fromInteger p) (M.adjust (subtract 1) 1 m)+    | otherwise              = Cons 0 M.empty++instance (ZeroTestable.C a, Field.C a, Eq a) => Field.C (T a) where+  recip (Cons 0 _)    = error "Monomial.recip: division by zero"+  recip (Cons a pows) = Cons (recip a) (M.map negate pows)
+ MathObj/MultiVarPolynomial.hs view
@@ -0,0 +1,156 @@+-- | Polynomials in a countably infinite set of variables x1, x2, x3, ...+module MathObj.MultiVarPolynomial +    ( -- * Type+      T(..)++      -- * Constructing polynomials+    , fromMonomials+    , lift0+    , lift1+    , lift2+    , x+    , constant++      -- * Operations++    , compose++    ) where++import qualified Algebra.Additive as Additive+import qualified Algebra.Ring as Ring+import qualified Algebra.ZeroTestable as ZeroTestable+import qualified Algebra.Differential as Differential++import qualified MathObj.Monomial as Mon++import qualified Data.Map as M++import NumericPrelude+import PreludeBase++-- | A polynomial is just a list of monomials, construed as their sum.+--   We maintain the invariant that polynomials are always sorted by+--   the ordering on monomials defined in "MathObj.Monomial": first by+--   partition degree, then by largest variable index (decreasing),+--   then by exponent of the highest-index variable (decreasing).+--   This works out nicely for operations on cycle index series.+--+--   Instances are provided for Additive, Ring, Differential+--   (partial differentiation with respect to x1), and Show.+newtype T a = Cons [Mon.T a]++instance (ZeroTestable.C a, Ring.C a, Ord a, Show a) => Show (T a) where+  show (Cons []) = "0"+  show (Cons (m:ms)) = show m ++ concatMap showMon ms+    where showMon m | Mon.coeff m < 0 = " - " ++ show (negate m)+                    | otherwise       = " + " ++ show m++{-# INLINE fromMonomials #-}+fromMonomials :: [Mon.T a] -> T a+fromMonomials = lift0++{-# INLINE lift0 #-}+lift0 :: [Mon.T a] -> T a+lift0 = Cons++{-# INLINE lift1 #-}+lift1 :: ([Mon.T a] -> [Mon.T a]) -> (T a -> T a)+lift1 f (Cons xs) = Cons (f xs)++{-# INLINE lift2 #-}+lift2 :: ([Mon.T a] -> [Mon.T a] -> [Mon.T a]) -> (T a -> T a -> T a)+lift2 f (Cons xs) (Cons ys) = Cons (f xs ys)++-- | Create the polynomial xn for a given n.+x :: (Ring.C a) => Integer -> T a+x n = fromMonomials [Mon.x n]++-- | Create a constant polynomial.+constant :: a -> T a+constant a = fromMonomials [Mon.constant a]++-- | Add two polynomials.  We assume that they are already sorted, so+--   that addition works on infinite polynomials.+add :: (Ord a, Additive.C a) => [a] -> [a] -> [a]+add xs ys = merge (+) xs ys++-- | Merge two sorted lists, with a combining function for elements+--   that are equal.+merge :: Ord a => (a -> a -> a) -> [a] -> [a] -> [a]+merge _ [] ys = ys+merge _ xs [] = xs+merge f xxs@(x:xs) yys@(y:ys) | x < y     = x : merge f xs yys+                              | x > y     = y : merge f xxs ys+                              | otherwise = (f x y) : merge f xs ys++instance (Additive.C a, ZeroTestable.C a) => Additive.C (T a) where+  zero   = fromMonomials []+  negate = lift1 $ map negate+  (+)    = lift2 add++-- | Multiply two (sorted) polynomials.+mul :: (Ring.C a, Ord a) => [a] -> [a] -> [a]+mul [] _ = []+mul _ [] = []+mul (x:xs) (y:ys) = x*y : add (map (x*) ys) (mul xs (y:ys))++instance (Ring.C a, ZeroTestable.C a) => Ring.C (T a) where+  fromInteger n = fromMonomials [fromInteger n]+  (*) = lift2 mul++-- Partial differentiation with respect to x1.+instance (ZeroTestable.C a, Ring.C a) => Differential.C (T a) where+  differentiate = lift1 $ filter (not . isZero) . map Differential.differentiate++-- | Plethyistic substitution: F o G = F(G(x1,x2,x3...),+--   G(x2,x4,x6...), G(x3,x6,x9...), ...)  See Bergeron, Labelle, and+--   Leroux, \"Combinatorial Species and Tree-Like Structures\",+--   p. 43.+compose :: (Ring.C a, ZeroTestable.C a) => T a -> T a -> T a+compose (Cons []) _ = Cons []+compose (Cons (x:_)) (Cons []) = Cons [x]+compose (Cons xs) yys@(Cons (y:ys)) +  | Mon.degree y == 0 = error "MultiVarPolynomial.compose: inner series must not have a constant term."+  | otherwise = comp xs yys++-- | We need to be careful to make sure this is suitably+--   lazy. For example, this works for finite polynomials:+--+-- > comp ms p = sum . map (substMon p) $ ms+--+--   but not for infinite ones!+--+--   This is accomplished by calling a recursive helper function+--   taking as an extra argument a running sum containing only terms+--   with partition degree greater than or equal to the most recently+--   processed monomial.  Plethyistically substituting a polynomial+--   (with no constant term) into a monomial of partition degree d+--   produces a polynomial with all terms of partition degree >= d, so+--   when we encounter a monomial with partition degree d, we know we+--   are done with all terms in the running sum of lesser partition+--   degree.+--+--   Precondition: the second argument has no constant term.+comp :: (Ring.C a, ZeroTestable.C a) => [Mon.T a] -> T a -> T a+comp ms p = comp' 0 ms+  where -- comp' :: T a -> [Mon.T a] -> T a+        comp' part []     = part+        comp' part (m:ms) = lift2 (++) done $ comp' (rest + substMon p m) ms+          where (done,rest) = splitPoly ((< Mon.pDegree m) . Mon.pDegree) part++-- | Plethyistic substitution of a polynomial into a monomial.+substMon :: (ZeroTestable.C a, Ring.C a) => T a -> Mon.T a -> T a+substMon poly m+  = (constant (Mon.coeff m) *)+  . M.foldWithKey (\sub pow -> (*) (scalePoly sub poly ^pow)) 1 +  $ Mon.powers m+  +-- | @scalePoly n Z@ changes Z(x_1, x_2, x_3, ...) into Z(x_n, x_2n, x_3n, ...)+scalePoly :: Integer -> T a -> T a+scalePoly n = lift1 $ map (Mon.scaleMon n)++-- | Split a polynomial into two pieces based on a predicate.+splitPoly :: (Mon.T a -> Bool) -> T a -> (T a, T a)+splitPoly p (Cons xs) = (Cons ys, Cons zs)+  where (ys, zs) = span p xs
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ np-extras.cabal view
@@ -0,0 +1,22 @@+name:           np-extras+version:        0.1+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.2.3+tested-with:    GHC == 6.10.3+author:         Brent Yorgey+maintainer:     Brent Yorgey <byorgey@cis.upenn.edu>+category:       Math+synopsis:       NumericPrelude extras+description:    Various extras to extend the NumericPrelude, including+                multivariate polynomials and factored rationals.++Library+  build-depends: base >= 3.0 && < 4.2, numeric-prelude >= 0.1.1 && < 0.2,+                 primes >= 0.1.1 && < 0.2, containers >= 0.2 && < 0.3+  exposed-modules:+    MathObj.FactoredRational+    MathObj.Monomial+    MathObj.MultiVarPolynomial+  extensions: NoImplicitPrelude