{-# LANGUAGE ConstraintKinds, DataKinds, FlexibleContexts, GADTs #-}
{-# LANGUAGE MultiParamTypeClasses, NoImplicitPrelude, ParallelListComp #-}
{-# LANGUAGE RankNTypes, ScopedTypeVariables, TypeOperators #-}
module Algebra.Algorithms.Groebner (
-- * Polynomial division
divModPolynomial, divPolynomial, modPolynomial
-- * Groebner basis
, calcGroebnerBasis, calcGroebnerBasisWith
, simpleBuchberger, reduceMinimalGroebnerBasis, minimizeGroebnerBasis
-- * Ideal operations
, isIdealMember, intersection, thEliminationIdeal
, quotIdeal, quotByPrincipalIdeal
, saturationIdeal, saturationByPrincipalIdeal
) where
import Algebra.Internal
import Algebra.Ring.Noetherian
import Algebra.Ring.Polynomial
import Data.List
import Numeric.Algebra
import Prelude hiding (Num (..), recip)
-- | Calculate a polynomial quotient and remainder w.r.t. second argument.
divModPolynomial :: (IsMonomialOrder order, IsPolynomial r n, Field r)
=> OrderedPolynomial r order n -> [OrderedPolynomial r order n] -> ([(OrderedPolynomial r order n, OrderedPolynomial r order n)], OrderedPolynomial r order n)
divModPolynomial f0 fs = loop f0 zero (zip (nub fs) (repeat zero))
where
loop p r dic
| p == zero = (dic, r)
| otherwise =
let ltP = toPolynomial $ leadingTerm p
in case break ((`divs` leadingMonomial p) . leadingMonomial . fst) dic of
(_, []) -> loop (p - ltP) (r + ltP) dic
(xs, (g, old):ys) ->
let q = toPolynomial $ leadingTerm p `tryDiv` leadingTerm g
dic' = xs ++ (g, old + q) : ys
in loop (p - (q * g)) r dic'
-- | Remainder of given polynomial w.r.t. the second argument.
modPolynomial :: (IsPolynomial r n, Field r, IsMonomialOrder order)
=> OrderedPolynomial r order n
-> [OrderedPolynomial r order n]
-> OrderedPolynomial r order n
modPolynomial = (snd .) . divModPolynomial
-- | A Quotient of given polynomial w.r.t. the second argument.
divPolynomial :: (IsPolynomial r n, Field r, IsMonomialOrder order)
=> OrderedPolynomial r order n
-> [OrderedPolynomial r order n]
-> [(OrderedPolynomial r order n, OrderedPolynomial r order n)]
divPolynomial = (fst .) . divModPolynomial
infixl 7 `divPolynomial`
infixl 7 `modPolynomial`
infixl 7 `divModPolynomial`
-- | Apply Buchberger's algorithm and calculate Groebner basis for the given ideal.
simpleBuchberger :: (Field k, IsPolynomial k n, IsMonomialOrder order)
=> Ideal (OrderedPolynomial k order n) -> [OrderedPolynomial k order n]
simpleBuchberger ideal =
let gs = nub $ generators ideal
in fst $ until (null . snd) (\(ggs, acc) -> let cur = nub $ ggs ++ acc in
(cur, calc cur)) (gs, calc gs)
where
calc acc = [ q | f <- acc, g <- acc, f /= g
, let q = sPolynomial f g `modPolynomial` acc, q /= zero
]
-- | Minimize the given groebner basis.
minimizeGroebnerBasis :: (Field k, IsPolynomial k n, IsMonomialOrder order)
=> [OrderedPolynomial k order n] -> [OrderedPolynomial k order n]
minimizeGroebnerBasis = foldr step []
where
step x xs = injectCoeff (recip $ leadingCoeff x) * x : filter (not . (leadingMonomial x `divs`) . leadingMonomial) xs
-- | Reduce minimum Groebner basis into reduced Groebner basis.
reduceMinimalGroebnerBasis :: (Field k, IsPolynomial k n, IsMonomialOrder order)
=> [OrderedPolynomial k order n] -> [OrderedPolynomial k order n]
reduceMinimalGroebnerBasis bs = filter (/= zero) $ map red bs
where
red x = x `modPolynomial` delete x bs
-- | Caliculating reduced Groebner basis of the given ideal w.r.t. the specified monomial order.
calcGroebnerBasisWith :: (Field k, IsPolynomial k n, IsMonomialOrder order, IsMonomialOrder order')
=> order -> Ideal (OrderedPolynomial k order' n) -> [OrderedPolynomial k order n]
calcGroebnerBasisWith ord i = calcGroebnerBasis $ mapIdeal (changeOrder ord) i
-- | Caliculating reduced Groebner basis of the given ideal w.r.t. the graded reversed lexicographic order.
calcGroebnerBasis :: (Field k, IsPolynomial k n, IsMonomialOrder order)
=> Ideal (OrderedPolynomial k order n) -> [OrderedPolynomial k order n]
calcGroebnerBasis = reduceMinimalGroebnerBasis . minimizeGroebnerBasis . simpleBuchberger
-- | Test if the given polynomial is the member of the ideal.
isIdealMember :: (IsPolynomial k n, Field k, IsMonomialOrder o)
=> OrderedPolynomial k o n -> Ideal (OrderedPolynomial k o n) -> Bool
isIdealMember f ideal = groebnerTest f (calcGroebnerBasis ideal)
-- | Test if the given polynomial can be divided by the given polynomials.
groebnerTest :: (IsPolynomial k n, Field k, IsMonomialOrder order)
=> OrderedPolynomial k order n -> [OrderedPolynomial k order n] -> Bool
groebnerTest f fs = f `modPolynomial` fs == zero
-- | Calculate n-th elimination ideal.
thEliminationIdeal :: ( IsMonomialOrder ord, Field k, IsPolynomial k m, IsPolynomial k (m :-: n)
, (n :<<= m) ~ True)
=> SNat n
-> Ideal (OrderedPolynomial k ord m)
-> Ideal (OrderedPolynomial k Lex (m :-: n))
thEliminationIdeal n ideal =
toIdeal $ [transformMonomial (dropV n) f | f <- calcGroebnerBasisWith Lex ideal
, all (all (== 0) . take (toInt n) . toList . snd) $ getTerms f
]
-- | An intersection ideal of given ideals.
intersection :: forall r k n ord.
( IsMonomialOrder ord, Field r, IsPolynomial r k, IsPolynomial r n
, IsPolynomial r (k :+: n)
)
=> Vector (Ideal (OrderedPolynomial r ord n)) k
-> Ideal (OrderedPolynomial r Lex n)
intersection Nil = Ideal $ singletonV one
intersection idsv@(_ :- _) =
let sk = sLengthV idsv
sn = sing :: SNat n
ts = genVars (sk %+ sn)
tis = zipWith (\ideal t -> mapIdeal ((t *) . shiftR sk) ideal) (toList idsv) ts
j = foldr appendIdeal (principalIdeal (one - foldr (+) zero ts)) tis
in case plusMinusEqR sn sk of
Eql -> case propToBoolLeq (plusLeqL sk sn) of
LeqTrueInstance -> sk `thEliminationIdeal` j
-- | Ideal quotient by a principal ideals.
quotByPrincipalIdeal :: (Field k, IsPolynomial k n, IsMonomialOrder ord)
=> Ideal (OrderedPolynomial k ord n)
-> OrderedPolynomial k ord n
-> Ideal (OrderedPolynomial k Lex n)
quotByPrincipalIdeal i g =
case intersection (i :- (Ideal $ singletonV g) :- Nil) of
Ideal gs -> Ideal $ mapV (snd . head . (`divPolynomial` [changeOrder Lex g])) gs
-- | Ideal quotient by the given ideal.
quotIdeal :: forall k ord n. (IsPolynomial k n, Field k, IsMonomialOrder ord)
=> Ideal (OrderedPolynomial k ord n)
-> Ideal (OrderedPolynomial k ord n)
-> Ideal (OrderedPolynomial k Lex n)
quotIdeal i (Ideal g) =
case singInstance (sLengthV g) of
SingInstance ->
case singInstance (sLengthV g %+ (sing :: SNat n)) of
SingInstance -> intersection $ mapV (i `quotByPrincipalIdeal`) g
-- | Saturation by a principal ideal.
saturationByPrincipalIdeal :: (Field k, IsPolynomial k n, IsMonomialOrder ord)
=> Ideal (OrderedPolynomial k ord n)
-> OrderedPolynomial k ord n -> Ideal (OrderedPolynomial k Lex n)
saturationByPrincipalIdeal is g =
case propToClassLeq $ leqSucc (sDegree g) of
LeqInstance -> sOne `thEliminationIdeal` addToIdeal (one - (castPolynomial g * var sOne)) (mapIdeal (shiftR sOne) is)
-- | Saturation ideal
saturationIdeal :: forall k ord n. (IsPolynomial k n, Field k, IsMonomialOrder ord)
=> Ideal (OrderedPolynomial k ord n)
-> Ideal (OrderedPolynomial k ord n)
-> Ideal (OrderedPolynomial k Lex n)
saturationIdeal i (Ideal g) =
case singInstance (sLengthV g) of
SingInstance ->
case singInstance (sLengthV g %+ (sing :: SNat n)) of
SingInstance -> intersection $ mapV (i `saturationByPrincipalIdeal`) g