{-| Module : Clif.Internals Copyright : (c) Matti A. Eskelinen, 2016 License : MIT Maintainer : matti.a.eskelinen@gmail.com Stability : experimental Portability : POSIX This module implements the various 'Clif' operations on the underlying type. Currently 'Clif' is implemented on top of "Data.Map", with blades as keys and scalar multipliers as values. = Warning __This module is not intended to be imported by end users and may change drastically in the future. It is currently exposed (and documented) only to help development.__ As development continues, some of the definitions here may be exported from the other modules. Comments and suggestions are welcomed. -} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Clif.Internal where import Clif.Basis import Control.Applicative import qualified Data.Map as M import Data.List (nub, intercalate) import Data.Map (Map) import Data.Function (on) -- * Type @Clif@ -- | A data type representing a Clif (multivector) composed of direct sum of scaled blades newtype Clif b a = Clif {unClif :: Map [b] a} deriving (Functor) -- * Constructors -- | Constructs a 'Clif' from a list of blades and their multipliers in canonical form. -- -- >>> fromList [([], 42), ([E 1, E 2], 1)] -- 42 *: [] + 1 *: [E 1,E 2] -- fromList :: (Eq a, Basis b a) => [([b], a)] -> Clif b a fromList = Clif . canon' . M.fromListWith (+) -- | Constructor for a blade blade :: (Eq a, Basis b a) => [b] -> a -> Clif b a blade = Clif .: M.singleton -- | Infix synonym for @'flip' 'blade'@ infix 9 *: (*:) :: (Eq a, Basis b a) => a -> [b] -> Clif b a (*:) = flip blade -- | Constructor for basis vector values. -- Note that @'vec' a s@ is equivalent to @'blade' [a] s@. vec :: (Eq a, Basis b a) => b -> a -> Clif b a vec = Clif .: M.singleton . (:[]) -- | 'Show' instance just shows the underlying 'Map' for now instance (Show b, Show a) => Show (Clif b a) where show = intercalate " + " . map showBlade . M.assocs . unClif where showBlade (b, s) = show s ++ " *: " ++ show b -- | The Eq instance calculates the canonical forms of the compared Clifs before comparison. instance (Eq b, Eq a, Basis b a) => Eq (Clif b a) where (==) = (==) `on` (unClif . canon) -- | Note that abs and signum are only well-defined on the scalar component of each Clif, and zero otherwise. instance (Eq a, Basis b a) => Num (Clif b a) where (+) = gPlus (*) = gMul abs = fmap abs . grade 0 negate = fmap negate signum = fmap signum . grade 0 fromInteger = blade mempty . fromInteger -- | Inverse elements only exist for Clifs c for which c times c is scalar. For others, recip does not terminate. instance (Eq a, Basis b a, Fractional a) => Fractional (Clif b a) where fromRational = blade mempty . fromRational recip v | isScalar v = fmap recip v | otherwise = revM . recip $ revM v where revM = gMul (rev v) -- * Operations -- | The Clifford (geometric) product on 'Clif's. gMul :: (Eq a, Basis b a) => Clif b a -> Clif b a -> Clif b a gMul = Clif .: gMul' `on` unClif -- | The Clifford product on 'Map's of blades and multipliers. Filter out zero values gMul' :: (Eq a, Basis b a) => Map [b] a -> Map [b] a -> Map [b] a gMul' = M.filter (/= 0) . M.fromListWith (+) . concat .: liftA2 basisMul `on` M.assocs -- | Addition of 'Clif' values (direct sum). gPlus :: (Eq a, Basis b a) => Clif b a -> Clif b a -> Clif b a gPlus = Clif .: gPlus' `on` unClif -- | Direct sum of matching keys from two 'Map's. Filter out zero values. gPlus' :: (Eq a, Basis b a) => Map [b] a -> Map [b] a -> Map [b] a gPlus' = M.filter (/= 0) .: M.unionWith (+) -- | Reverse of a 'Clif', i.e. the reverse of all its component blades. rev :: Ord b => Clif b a -> Clif b a rev = Clif . rev' . unClif -- | Reverses each blade (key) rev' :: Ord b => Map [b] a -> Map [b] a rev' = M.mapKeys reverse -- | Returns the canonical form of a 'Clif' canon :: (Eq a, Basis b a) => Clif b a -> Clif b a canon = Clif . canon' . unClif -- | Returns the canonical representation of a 'Clif' (blades simplified and in canonical order) canon' :: (Eq a, Basis b a) => Map [b] a -> Map [b] a canon' = M.filter (/=0) . M.fromListWith (+) . concatMap canonical . M.assocs -- | Grade projection on the given grade. For negative values, returns zero. -- -- Note that this always calculates the canonical form of a Clif before projecting it. grade :: (Eq a, Basis b a) => Int -> Clif b a -> Clif b a grade k = Clif . grade' k . unClif -- | Filter blades (keys) by their length. grade' :: (Eq a, Basis b a) => Int -> Map [b] a -> Map [b] a grade' k = M.filterWithKey f . canon' where f = const . (==) k . length -- | General product or contraction of Clifs using a given grade function. -- Given a function @f :: 'Int' -> 'Int' -> 'Int'@ and p, q-grade Clifs A and B, contractWith returns -- the f(p,q)-grade projection of the 'Clif' A times B. contractWith :: (Eq a, Basis b a) => (Int -> Int -> Int) -> Clif b a -> Clif b a -> Clif b a contractWith f = sum .: liftA2 g `on` grades where g (r, x) (s, y) = let k = f r s in case compare k 0 of LT -> 0 _ -> grade k $ x * y -- * Properties -- | List of nonzero grades. -- -- Note that this always calculates the canonical form of a Clif before recovering the filled grades. filledGrades :: (Eq a, Basis b a) => Clif b a -> [Int] filledGrades = filledGrades' . unClif -- | List of nonzero grades -- -- Note that this always calculates the canonical form of a Clif before recovering the filled grades. filledGrades' :: (Eq a, Basis b a) => Map [b] a -> [Int] filledGrades' = nub . map length . M.keys . canon' -- | Returns a list containing each non-zero grade component of a 'Clif' and it's grade as an 'Int'. -- -- Note that this always calculates the canonical form of a Clif before testing any operations. grades :: (Eq a, Basis b a) => Clif b a -> [(Int, Clif b a)] grades v = [(k, grade k v) | k <- filledGrades v] -- | True if the 'Clif' contains no nonzero blades of grade greater than zero. -- -- Note that this always calculates the canonical form of a Clif before testing whether it is scalar. isScalar :: (Eq a, Basis b a) => Clif b a -> Bool isScalar v = ((0==) . maxGrade) v || isZero v -- | True for a zero multivector. -- -- Note that this always calculates the canonical form of a Clif before testing whether it is zero. isZero :: (Eq a, Basis b a) => Clif b a -> Bool isZero = null . canon' . unClif -- | The highest nonempty nonzero grade of a Clif. -- -- Note that this always calculates the canonical form of a Clif before recovering the highest grade. maxGrade :: (Eq a, Basis b a) => Clif b a -> Int maxGrade v | isZero v = 0 | otherwise = maximum $ filledGrades v -- * Utility functions {-# INLINE (.:) #-} infixl 8 .: -- |Composition of unary and binary functions, highly useful since two-parameter constructors are ubiquitous here. Redefined here to skip extra dependencies. -- -- prop> (f .: g) x y = f (g x y) -- (.:) :: (a -> b) -> (c -> d -> a) -> c -> d -> b (.:) = (.) . (.)