module Algebra.Ring.Polynomial
( Polynomial, Monomial, MonomialOrder, Order
, lex, revlex, graded, grlex, grevlex, transformMonomial
, IsPolynomial, coeff, lcmMonomial, sPolynomial, polynomial
, castMonomial, castPolynomial, toPolynomial, changeOrder
, scastMonomial, scastPolynomial, OrderedPolynomial, showPolynomialWithVars
, normalize, injectCoeff, varX, var, getTerms, shiftR
, divs, tryDiv, fromList
, leadingTerm, leadingMonomial, leadingCoeff, genVars, sDegree
, OrderedMonomial(..), Grevlex(..), Revlex(..), Lex(..), Grlex(..)
, IsOrder, IsMonomialOrder) where
import Algebra.Internal
import Algebra.Ring.Noetherian
import Control.Arrow
import Control.Lens
import Data.List (intercalate)
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe
import Data.Monoid
import Data.Ord
import Data.Proxy
import Numeric.Algebra
import Prelude hiding (lex, (*), (+), (), (^), (^^), recip, negate)
import qualified Prelude as P
type Monomial (n :: Nat) = Vector Int n
fromList :: SNat n -> [Int] -> Monomial n
fromList SZ _ = Nil
fromList (SS n) [] = 0 :- fromList n []
fromList (SS n) (x : xs) = x :- fromList n xs
type MonomialOrder n = Monomial n -> Monomial n -> Ordering
totalDegree :: Monomial n -> Int
totalDegree = foldrV (+) 0
lex :: MonomialOrder n
lex Nil Nil = EQ
lex (x :- xs) (y :- ys) = x `compare` y <> xs `lex` ys
lex _ _ = error "cannot happen"
revlex :: Monomial n -> Monomial n -> Ordering
revlex (x :- xs) (y :- ys) = xs `revlex` ys <> y `compare` x
revlex Nil Nil = EQ
revlex _ _ = error "cannot happen!"
graded :: (Monomial n -> Monomial n -> Ordering) -> (Monomial n -> Monomial n -> Ordering)
graded cmp xs ys = comparing totalDegree xs ys <> cmp xs ys
grlex :: MonomialOrder n
grlex = graded lex
grevlex :: MonomialOrder n
grevlex = graded revlex
newtype OrderedMonomial (ordering :: *) n = OrderedMonomial { getMonomial :: Monomial n }
deriving instance (Eq (Monomial n)) => Eq (OrderedMonomial ordering n)
instance Wrapped (Monomial n) (Monomial m) (OrderedMonomial o n) (OrderedMonomial o' m) where
wrapped = iso OrderedMonomial getMonomial
class IsOrder (ordering :: *) where
cmpMonomial :: Proxy ordering -> MonomialOrder n
data Grevlex = Grevlex
deriving (Show, Eq, Ord)
data Lex = Lex
deriving (Show, Eq, Ord)
data Grlex = Grlex
deriving (Show, Eq, Ord)
data Revlex = Revlex
deriving (Show, Eq, Ord)
instance IsOrder Grevlex where
cmpMonomial _ = grevlex
instance IsOrder Revlex where
cmpMonomial _ = revlex
instance IsOrder Lex where
cmpMonomial _ = lex
instance IsOrder Grlex where
cmpMonomial _ = grlex
class IsOrder name => IsMonomialOrder name where
instance IsMonomialOrder Grlex
instance IsMonomialOrder Grevlex
instance IsMonomialOrder Lex
instance (Eq (Monomial n), IsOrder name) => Ord (OrderedMonomial name n) where
OrderedMonomial m `compare` OrderedMonomial n = cmpMonomial (Proxy :: Proxy name) m n
instance (Eq (Monomial n)) => Ord (Monomial n) where
compare = grevlex
newtype OrderedPolynomial r order n = Polynomial { terms :: Map (OrderedMonomial order n) r }
type Polynomial r = OrderedPolynomial r Grevlex
type IsPolynomial r n = (NoetherianRing r, Sing n, Eq r)
coeff :: (IsOrder order, IsPolynomial r n) => Monomial n -> OrderedPolynomial r order n -> r
coeff d = M.findWithDefault zero (OrderedMonomial d) . terms
instance Wrapped (Map (OrderedMonomial order n) r) (Map (OrderedMonomial order' m) q)
(OrderedPolynomial r order n) (OrderedPolynomial q order' m) where
wrapped = iso Polynomial terms
castMonomial :: (IsOrder o, IsOrder o', Sing m, n :<= m) => OrderedMonomial o n -> OrderedMonomial o' m
castMonomial = unwrapped %~ fromList sing . toList
scastMonomial :: (n :<= m) => SNat m -> OrderedMonomial o n -> OrderedMonomial o m
scastMonomial snat = unwrapped %~ fromList snat . toList
castPolynomial :: (IsPolynomial r n, IsPolynomial r m, Sing m, IsOrder o, IsOrder o', n :<= m)
=> OrderedPolynomial r o n
-> OrderedPolynomial r o' m
castPolynomial = unwrapped %~ M.mapKeys castMonomial
scastPolynomial :: (IsOrder o, IsOrder o', IsPolynomial r n, IsPolynomial r m, n :<= m, Sing m)
=> SNat m -> OrderedPolynomial r o n -> OrderedPolynomial r o' m
scastPolynomial _ = castPolynomial
normalize :: (Eq r, IsOrder order, IsPolynomial r n)
=> OrderedPolynomial r order n -> OrderedPolynomial r order n
normalize = unwrapped %~ M.insertWith (+) (OrderedMonomial $ fromList sing []) zero . M.filter (/= zero)
instance (Eq r, IsOrder order, IsPolynomial r n) => Eq (OrderedPolynomial r order n) where
(normalize -> Polynomial f) == (normalize -> Polynomial g) = f == g
injectCoeff :: (IsPolynomial r n) => r -> OrderedPolynomial r order n
injectCoeff r = Polynomial $ M.singleton (OrderedMonomial $ fromList sing []) r
instance (IsOrder order, IsPolynomial r n) => NoetherianRing (OrderedPolynomial r order n) where
instance (IsOrder order, IsPolynomial r n) => Ring (OrderedPolynomial r order n) where
instance (IsOrder order, IsPolynomial r n) => Rig (OrderedPolynomial r order n) where
instance (IsOrder order, IsPolynomial r n) => Group (OrderedPolynomial r order n) where
negate (Polynomial dic) = Polynomial $ fmap negate dic
instance (IsOrder order, IsPolynomial r n) => LeftModule Integer (OrderedPolynomial r order n) where
n .* Polynomial dic = Polynomial $ fmap (n .*) dic
instance (IsOrder order, IsPolynomial r n) => RightModule Integer (OrderedPolynomial r order n) where
(*.) = flip (.*)
instance (IsOrder order, IsPolynomial r n) => Additive (OrderedPolynomial r order n) where
(Polynomial f) + (Polynomial g) = normalize $ Polynomial $ M.unionWith (+) f g
instance (IsOrder order, IsPolynomial r n) => Monoidal (OrderedPolynomial r order n) where
zero = injectCoeff zero
instance (IsOrder order, IsPolynomial r n) => LeftModule Natural (OrderedPolynomial r order n) where
n .* Polynomial dic = Polynomial $ fmap (n .*) dic
instance (IsOrder order, IsPolynomial r n) => RightModule Natural (OrderedPolynomial r order n) where
(*.) = flip (.*)
instance (IsOrder order, IsPolynomial r n) => Unital (OrderedPolynomial r order n) where
one = injectCoeff one
instance (IsOrder order, IsPolynomial r n) => Multiplicative (OrderedPolynomial r order n) where
Polynomial (M.toList -> d1) * Polynomial (M.toList -> d2) =
let dic = [ (OrderedMonomial $ zipWithV (+) a b, r * r') | (getMonomial -> a, r) <- d1, (getMonomial -> b, r') <- d2 ]
in normalize $ Polynomial $ M.fromListWith (+) dic
instance (IsOrder order, IsPolynomial r n) => Semiring (OrderedPolynomial r order n) where
instance (IsOrder order, IsPolynomial r n) => Commutative (OrderedPolynomial r order n) where
instance (IsOrder order, IsPolynomial r n) => Abelian (OrderedPolynomial r order n) where
instance (Eq r, IsPolynomial r n, IsOrder order, Show r) => Show (OrderedPolynomial r order n) where
show = showPolynomialWithVars [(n, "X_"++ show n) | n <- [1..]]
showPolynomialWithVars :: (Eq a, Show a, Sing n, NoetherianRing a, IsOrder ordering)
=> [(Int, String)] -> OrderedPolynomial a ordering n -> String
showPolynomialWithVars dic p0@(Polynomial d)
| p0 == zero = "0"
| otherwise = intercalate " + " $ mapMaybe showTerm $ M.toDescList d
where
showTerm (getMonomial -> deg, c)
| c == zero = Nothing
| otherwise =
let cstr = if (c == zero one)
then if any (/= zero) (toList deg) then "-" else "-1"
else if (c /= one || isConstantMonomial deg)
then show c ++ " "
else ""
in Just $ cstr ++ unwords (mapMaybe showDeg (zip [1..] $ toList deg))
showDeg (n, p) | p == 0 = Nothing
| p == 1 = Just $ showVar n
| otherwise = Just $ showVar n ++ "^" ++ show p
showVar n = fromMaybe ("X_" ++ show n) $ lookup n dic
isConstantMonomial :: (Eq a, Num a) => Vector a n -> Bool
isConstantMonomial v = all (== 0) $ toList v
instance (IsMonomialOrder order, IsPolynomial r n, Num r) => Num (OrderedPolynomial r order n) where
(+) = (Numeric.Algebra.+)
(*) = (Numeric.Algebra.*)
fromInteger = injectCoeff . P.fromInteger
signum f = if f == zero then zero else injectCoeff 1
abs = id
negate = ((P.negate 1 :: Integer) .*)
varX :: (NoetherianRing r, Sing n, One :<= n) => OrderedPolynomial r order n
varX = Polynomial $ M.singleton (OrderedMonomial $ fromList sing [1]) one
var :: (NoetherianRing r, Sing m, S n :<= m) => SNat (S n) -> OrderedPolynomial r order m
var vIndex = Polynomial $ M.singleton (OrderedMonomial $ fromList sing (buildIndex vIndex)) one
toPolynomial :: (IsOrder order, IsPolynomial r n) => (r, Monomial n) -> OrderedPolynomial r order n
toPolynomial (c, deg) = Polynomial $ M.singleton (OrderedMonomial deg) c
polynomial :: (Sing n, Eq r, NoetherianRing r, IsOrder order) => Map (OrderedMonomial order n) r -> OrderedPolynomial r order n
polynomial dic = normalize $ Polynomial dic
buildIndex :: SNat (S n) -> [Int]
buildIndex (SS SZ) = [1]
buildIndex (SS (SS n)) = 0 : buildIndex (SS n)
leadingTerm :: (IsOrder order, IsPolynomial r n)
=> OrderedPolynomial r order n -> (r, Monomial n)
leadingTerm (Polynomial d) =
case M.maxViewWithKey d of
Just ((deg, c), _) -> (c, getMonomial deg)
Nothing -> (zero, fromList sing [])
leadingMonomial :: (IsOrder order, IsPolynomial r n) => OrderedPolynomial r order n -> Monomial n
leadingMonomial = snd . leadingTerm
leadingCoeff :: (IsOrder order, IsPolynomial r n) => OrderedPolynomial r order n -> r
leadingCoeff = fst . leadingTerm
divs :: Monomial n -> Monomial n -> Bool
xs `divs` ys = and $ toList $ zipWithV (<=) xs ys
tryDiv :: Field r => (r, Monomial n) -> (r, Monomial n) -> (r, Monomial n)
tryDiv (a, f) (b, g)
| g `divs` f = (a * recip b, zipWithV () f g)
| otherwise = error "cannot divide."
lcmMonomial :: Monomial n -> Monomial n -> Monomial n
lcmMonomial = zipWithV max
sPolynomial :: (IsPolynomial k n, Field k, IsOrder order)
=> OrderedPolynomial k order n
-> OrderedPolynomial k order n -> OrderedPolynomial k order n
sPolynomial f g =
let h = (one, lcmMonomial (leadingMonomial f) (leadingMonomial g))
in toPolynomial (h `tryDiv` leadingTerm f) * f toPolynomial (h `tryDiv` leadingTerm g) * g
changeOrder :: (Eq (Monomial n), IsOrder o, IsOrder o', Sing n)
=> o' -> OrderedPolynomial k o n -> OrderedPolynomial k o' n
changeOrder _ = unwrapped %~ M.mapKeys (OrderedMonomial . getMonomial)
getTerms :: OrderedPolynomial k order n -> [(k, Monomial n)]
getTerms = map (snd &&& getMonomial . fst) . M.toDescList . terms
transformMonomial :: (IsOrder o, IsPolynomial k n, IsPolynomial k m)
=> (Monomial n -> Monomial m) -> OrderedPolynomial k o n -> OrderedPolynomial k o m
transformMonomial trans (Polynomial d) = Polynomial $ M.mapKeys (OrderedMonomial . trans . getMonomial) d
shiftR :: forall k r n ord. (Field r, IsPolynomial r n, IsPolynomial r (k :+: n), IsOrder ord)
=> SNat k -> OrderedPolynomial r ord n -> OrderedPolynomial r ord (k :+: n)
shiftR k =
case singInstance k of
SingInstance -> transformMonomial (appendV (fromList k []))
genVars :: forall k o n. (IsPolynomial k (S n), IsOrder o)
=> SNat (S n) -> [OrderedPolynomial k o (S n)]
genVars sn =
let n = toInt sn
seed = cycle $ 1 : replicate (n 1) 0
in map (\m -> Polynomial $ M.singleton (OrderedMonomial $ fromList sn $ take n (drop (nm) seed)) one) [0..n1]
sDegree :: OrderedPolynomial k ord n -> SNat n
sDegree (Polynomial dic) = sLengthV $ getMonomial $ fst $ M.findMin dic