packages feed

gasp-1.4.0.0: Algebra/Morphism/LinComb.hs

{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module Algebra.Morphism.LinComb where

import Prelude hiding (Num(..), sum)
-- import Data.List (intercalate,and,filter)
import Algebra.Classes
import qualified Data.Map as M
-- import Data.Function (on)
-- import Data.Monoid
-- import Control.Applicative
-- import Data.Traversable

-- | Normalised linear combinations as maps from variables to
-- coefficients (zero coefficient never present in the map)
newtype LinComb x c = LinComb (M.Map x c)
  deriving (Functor,AbelianAdditive,Eq,Ord,Show,Traversable,Foldable)
deriving instance {-# Overlappable #-} Scalable s a => Scalable s (LinComb k a)

fromLinComb :: LinComb x c -> M.Map x c
fromLinComb (LinComb x) = x
eval :: forall d x c v. Scalable d x => Additive x => (c -> d) -> (v -> x) -> LinComb v c -> x
eval fc fv p = sum [ fc c *^ fv v | (v, c) <- toList p ]

normalise :: DecidableZero c => LinComb x c -> LinComb x c
normalise (LinComb x) = LinComb (M.filter (not . isZero) x)

instance (AbelianAdditive c,DecidableZero c,Ord e) => Additive (LinComb e c) where
  zero = LinComb zero
  LinComb x + LinComb y = normalise (LinComb (x+y))

instance (AbelianAdditive c,Group c,DecidableZero c,Ord e) => Group (LinComb e c) where
  negate = fmap negate
  LinComb x - LinComb y = normalise (LinComb (x-y))

-- Alternative instances for non-normalised version:
-- instance (Eq e, Eq c, Additive c) => Eq (LinComb e c) where
--    (==) = (==) `on` toList

-- instance (Ord e, Ord c, Additive c) => Ord (LinComb e c) where
--    compare = compare `on` toList

toList :: LinComb k a -> [(k, a)]
toList = {- filter ((/= zero) . snd)  no need to filter zeros because normalised -} M.assocs . fromLinComb 

var :: Multiplicative c => x -> LinComb x c
var x = LinComb (M.singleton x one)

-- | Convert from list without testing coefficients
unsafeFromList :: Ord v => [(v,c)] -> LinComb v c
unsafeFromList = LinComb . M.fromList

fromList :: DecidableZero c => Additive c => Ord v => [(v,c)] -> LinComb v c
fromList = normalise . LinComb . M.fromListWith (+)

instance (AbelianAdditive c, Eq c, DecidableZero c, Ord e) => DecidableZero (LinComb e c) where
  isZero (LinComb p) = p == M.empty  

-- instance (Show c, Show e, Eq c, Multiplicative c) => Show (LinComb e c) where
--   show (LinComb xs) = intercalate "+" ([(if coef /= one then show coef else mempty) <> show m  | (m,coef) <- M.toList xs])

-- | Substitution by evaluation
subst :: DecidableZero c => AbelianAdditive c => Scalable c c => Ord v => (x -> LinComb v c) -> LinComb x c -> LinComb v c
subst f = eval id f

-- | transform variables. coefficients are not touched
mapVars :: Ord x => (t -> x) -> LinComb t c -> LinComb x c
mapVars f (LinComb m) = unsafeFromList [(f x, e) | (x,e) <- M.assocs m]

-- | Multiplies elements, assuming multiplication is monotonous.
mulVarsMonotonic :: Multiplicative x => x -> LinComb x c -> LinComb x c
mulVarsMonotonic x (LinComb m) = LinComb (M.mapKeysMonotonic (x *) m) 

-- | transform variables with effect. coefficients are not touched
traverseVars :: Applicative f => Ord x => (v -> f x) -> LinComb v c -> f (LinComb x c)
traverseVars f e = unsafeFromList <$> traverse (\(x,c) -> (,c) <$> f x) (toList e)

-- | transform variables and coefficients with effect.
bitraverse :: Applicative f => Ord x => (v -> f x) -> (c -> f d) -> LinComb v c -> f (LinComb x d)
bitraverse f g e = unsafeFromList <$> traverse (\(x,c) -> (,) <$> f x <*> g c) (toList e)