\documentclass{article} %include polycode.fmt \usepackage{fontspec} \usepackage{amsmath} \usepackage{unicode-math} \usepackage{lualatex-math} \setmainfont{latinmodern-math.otf} \setmathfont{latinmodern-math.otf} \usepackage{verbatim} \author{Sophie Taylor} \title{haskell-clifford: A Haskell Clifford algebra dynamics library} \begin{document} So yeah. This is a Clifford number representation. I will fill out the documentation more fully and stuff once the design has stabilised. I am basing the design of this on Issac Trotts' geometric algebra library.\cite{hga} Let us begin. We are going to use the Numeric Prelude because it is (shockingly) nicer for numeric stuff. \begin{code}
{-# LANGUAGE NoImplicitPrelude, FlexibleContexts, RankNTypes, ScopedTypeVariables, DeriveDataTypeable #-}
{-# LANGUAGE NoMonomorphismRestriction, UnicodeSyntax, GADTs #-}
{-# LANGUAGE FlexibleInstances,  UnicodeSyntax, GADTs, KindSignatures, DataKinds #-}
{-# LANGUAGE TemplateHaskell, StandaloneDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
\end{code} %if False \begin{code}
{-# OPTIONS_GHC -fllvm -fexcess-precision -optlo-O3 -O3 -optlc-O=3 -Wall #-}
-- OPTIONS_GHC -Odph -fvectorise -package dph-lifted-vseg 
--  LANGUAGE ParallelArrays
\end{code} %endif Clifford algebras are a module over a ring. They also support all the usual transcendental functions. \begin{code}
module Numeric.Clifford.Blade where

import NumericPrelude hiding (iterate, head, map, tail, reverse, scanl, zipWith, drop, (++), filter, null, length, foldr, foldl1, zip, foldl, concat, (!!), concatMap,any, repeat, replicate, elem, replicate)
import Algebra.Laws
import Algebra.Absolute
import Algebra.Additive
import Algebra.Ring
import Data.Serialize
import Data.Word
import Data.List.Stream
import Data.Permute (sort, isEven)
import Numeric.Natural
import qualified NumericPrelude.Numeric as NPN
import Test.QuickCheck
import Control.Lens hiding (indices)
import Data.DeriveTH
import GHC.TypeLits hiding (isEven)
import GHC.Real (fromIntegral)
import Algebra.Field
import Debug.Trace
--trace _ a = a

\end{code} The first problem: How to represent basis blades. One way to do it is via generalised Pauli matrices. Another way is to use lists, which we will do because this is Haskell. >:0 \texttt{bScale} is the amplitude of the blade. \texttt{bIndices} are the indices for the basis. \begin{code}

data Blade (n :: Nat) f where
    Blade :: forall n f . (SingI n, Algebra.Field.C f) => {_scale :: f, _indices :: [Natural]} -> Blade n f

-- makeLenses ''Blade
scale :: Lens' (Blade n f) f
scale = lens _scale (\blade v -> blade {_scale = v})
indices :: Lens' (Blade n f) [Natural]
indices = lens _indices (\blade v -> blade {_indices = v})
dimension :: forall (n::Nat) f. SingI n => Blade n f ->  Natural
dimension _ = toNatural  ((GHC.Real.fromIntegral $ fromSing (sing :: Sing n))::Word)
bScale b =  b^.scale
bIndices b = b^.indices
instance(Show f) =>  Show (Blade n f) where
    --TODO: Do this with HaTeX
    show  (Blade scale indices) = pref ++  if null indices then "" else basis where
                        pref = show scale
                        basis =  foldr (++) "" textIndices
                        textIndices = map vecced indices
                        vecced index = "\\vec{e_{" ++ show index ++ "}}"
                                                
                        
instance (Algebra.Additive.C f, Eq f) => Eq (Blade n f) where
   a == b = aScale == bScale && aIndices == bIndices where
                 (Blade aScale aIndices) = bladeNormalForm a
                 (Blade bScale bIndices) = bladeNormalForm b

\end{code} For example, a scalar could be constructed like so: \texttt{Blade s []} \begin{code}
scalarBlade :: (Algebra.Field.C f, SingI n) => f -> Blade n f
scalarBlade d = Blade d []

zeroBlade :: (Algebra.Field.C f, SingI n) => Blade n f
zeroBlade = scalarBlade Algebra.Additive.zero

bladeNonZero b = b^.scale /= Algebra.Additive.zero

bladeNegate b = b&scale%~negate --Blade (Algebra.Additive.negate$ b^.scale) (b^.indices)

bladeScaleLeft s (Blade f ind) = Blade (s * f) ind
bladeScaleRight s (Blade f ind) = Blade (f * s) ind
\end{code} However, the plain data constructor should never be used, for it doesn't order them by default. It also needs to represent the vectors in an ordered form for efficiency and niceness. Further, due to skew-symmetry, if the vectors are in an odd permutation compared to the normal form, then the scale is negative. Additionally, since $\vec{e}_k^2 = 1$, pairs of them should be removed. \begin{align} \vec{e}_1∧...∧\vec{e}_k∧...∧\vec{e}_k∧... = 0\\ \vec{e}_2∧\vec{e}_1 = -\vec{e}_1∧\vec{e}_2\\ \vec{e}_k^2 = 1 \end{align} \begin{code}
bladeNormalForm :: forall (n::Nat) f. Blade n f -> Blade n f
bladeNormalForm (Blade scale indices)  = result 
        where
             result = if (any (\i -> (GHC.Real.fromIntegral i) >n') indices) then trace "Blade contains vector with i > d" zeroBlade else Blade scale' uniqueSorted
             n' = fromSing (sing :: Sing n)
             numOfIndices = length indices
             (sorted, perm) = Data.Permute.sort numOfIndices indices
             scale' = if isEven perm then scale else negate scale
             uniqueSorted = removeDupPairs sorted
                            where
                              removeDupPairs [] = []
                              removeDupPairs [x] = [x]
                              removeDupPairs (x:y:rest) | x == y = removeDupPairs rest
                                                        | otherwise = x : removeDupPairs (y:rest)
\end{code} What is the grade of a blade? Just the number of indices. \begin{code}
grade :: Blade n f -> Integer
grade = toInteger . length . bIndices 

bladeIsOfGrade :: Blade n f -> Integer -> Bool
blade `bladeIsOfGrade` k = grade blade == k

bladeGetGrade :: Integer -> Blade n f -> Blade n f
bladeGetGrade k blade@(Blade _ _) =
    if blade `bladeIsOfGrade` k then blade else zeroBlade
\end{code} First up for operations: Blade multiplication. This is no more than assembling orthogonal vectors into k-vectors. \begin{code}
bladeMul ::  Blade n f -> Blade n f-> Blade n f
bladeMul x@(Blade _ _) y@(Blade _ _)= bladeNormalForm $ Blade (bScale x Algebra.Ring.* bScale y) (bIndices x ++ bIndices y)
multiplyBladeList :: (SingI n, Algebra.Field.C f) => [Blade n f] -> Blade n f
multiplyBladeList [] = error "Empty blade list!"
multiplyBladeList (a:[]) = a
multiplyBladeList a = bladeNormalForm $ Blade scale indices where
    indices = concatMap bIndices a
    scale = foldl1 (*) (map bScale a)


\end{code} Next up: The outer (wedge) product, denoted by $∧$ :3 \begin{code}
bWedge :: Blade n f -> Blade n f -> Blade n f
bWedge x y = bladeNormalForm $ bladeGetGrade k xy
             where
               k = grade x + grade y
               xy = bladeMul x y

\end{code} Now let's do the inner (dot) product, denoted by $⋅$ :D \begin{code}
bDot ::Blade n f -> Blade n f -> Blade n f
bDot x y = bladeNormalForm $ bladeGetGrade k xy
          where
            k = Algebra.Absolute.abs $ grade x - grade y
            xy = bladeMul x y

propBladeDotAssociative = Algebra.Laws.associative bDot

\end{code} These are the three fundamental operations on basis blades. Now for linear combinations of (possibly different basis) blades. To start with, let's order basis blades: \begin{code}
instance (Algebra.Additive.C f, Ord f) => Ord (Blade n f) where
    compare a b | bIndices a == bIndices b = compare (bScale a) (bScale b)
                | otherwise =  compare (bIndices a) (bIndices b)


instance Arbitrary Natural where
    arbitrary = sized $ \n ->
                let n' = NPN.abs n in
                 fmap (toNatural . (\x -> (GHC.Real.fromIntegral x)::Word)) (choose (0, n'))
    shrink = shrinkIntegral
-- $(derive makeArbitrary ''Blade)
\end{code} Now for some testing of algebraic laws. \begin{code}

{- Note: Figure out what this is meant to be lol
skewcommutative op x y = x `op` y == (bladeScaleLeft (fromInteger (-1))$ y `op` x)

propAnticommutativeMultiplication :: (Eq f,Algebra.Ring.C f, Algebra.Additive.C f) => Blade f -> Blade f -> Bool
propAnticommutativeMultiplication = anticommutative bladeMul
-}
propCommutativeAddition = commutative (+)
\end{code} \bibliographystyle{IEEEtran} \bibliography{biblio.bib} \end{document}