safe-tensor-0.2.0.0: src/Math/Tensor/LinearAlgebra/Scalar.hs
{-# LANGUAGE Safe #-}
{-# LANGUAGE MultiParamTypeClasses #-}
-----------------------------------------------------------------------------
{-|
Module : Math.Tensor.LinearAlgebra.Scalar
Description : Scalar types for usage as Tensor values.
Copyright : (c) Nils Alex, 2020
License : MIT
Maintainer : nils.alex@fau.de
Scalar types for usage as Tensor values.
-}
-----------------------------------------------------------------------------
module Math.Tensor.LinearAlgebra.Scalar
( Lin(..)
, Poly(..)
, singletonPoly
, polyMap
, getVars
, shiftVars
, normalize
) where
import qualified Data.IntMap.Strict as IM
( IntMap
, singleton
, null
, keys
, map
, filter
, mapKeysMonotonic
, unionWith
, findMin
)
-- |Linear combination represented as mapping from
-- variable number to prefactor.
newtype Lin a = Lin (IM.IntMap a) deriving (Show, Ord, Eq)
-- |Polynomial: Can be constant, affine, or something of higher
-- rank which is not yet implemented.
data Poly a = Const !a -- ^ constant value
| Affine !a !(Lin a) -- ^ constant value plus linear term
| NotSupported -- ^ higher rank
deriving (Show, Ord, Eq)
-- |Produces an affine value \(c + a\cdot x_i\)
singletonPoly :: a -- ^ constant
-> Int -- ^ variable number
-> a -- ^ prefactor
-> Poly a
singletonPoly a i v = Affine a $ Lin $ IM.singleton i v
-- |Maps over 'Poly'
polyMap :: (a -> b) -> Poly a -> Poly b
polyMap f (Const a) = Const (f a)
polyMap f (Affine a (Lin lin)) = Affine (f a) $ Lin $ IM.map f lin
polyMap _ _ = NotSupported
instance (Num a, Eq a) => Num (Poly a) where
Const a + Const b = Const $ a + b
Const a + Affine b lin = Affine (a+b) lin
Affine a lin + Const b = Affine (a+b) lin
Affine a (Lin m1) + Affine b (Lin m2)
| IM.null m' = Const $ a + b
| otherwise = Affine (a+b) (Lin m')
where
m' = IM.filter (/=0) $ IM.unionWith (+) m1 m2
NotSupported + _ = NotSupported
_ + NotSupported = NotSupported
negate = polyMap negate
abs (Const a) = Const $ abs a
abs _ = NotSupported
signum (Const a) = Const $ signum a
signum _ = NotSupported
fromInteger = Const . fromInteger
Const a * Const b = Const $ a * b
Const a * Affine b (Lin lin)
| a == 0 = Const 0
| otherwise = Affine (a*b) $ Lin $ IM.map (a*) lin
Affine a (Lin lin) * Const b
| b == 0 = Const 0
| otherwise = Affine (a*b) $ Lin $ IM.map (*b) lin
_ * _ = NotSupported
-- |Returns list of variable numbers present in the polynomial.
getVars :: Poly a -> [Int]
getVars (Const _) = []
getVars NotSupported = []
getVars (Affine _ (Lin lm)) = IM.keys lm
-- |Shifts variable numbers in the polynomial by a constant value.
shiftVars :: Int -> Poly a -> Poly a
shiftVars _ (Const a) = Const a
shiftVars _ NotSupported = NotSupported
shiftVars s (Affine a (Lin lin)) =
Affine a $ Lin $ IM.mapKeysMonotonic (+s) lin
-- |Normalizes a polynomial:
-- \[
-- \mathrm{normalize}(c) = 1 \\
-- \mathrm{normalize}(c + a_1\cdot x_1 + a_2\cdot x_2 + \dots + a_n\cdot x_n) = \frac{c}{a_1} + 1\cdot x_1 + \frac{a_2}{a_1}\cdot x_2 + \dots + \frac{a_n}{a_1}\cdot x_n
-- \]
normalize :: Fractional a => Poly a -> Poly a
normalize (Const _) = Const 1
normalize NotSupported = NotSupported
normalize (Affine a (Lin lin)) = Affine (a/v) $ Lin $ IM.map (/v) lin
where
(_,v) = IM.findMin lin