dynobud-1.9.0.0: src/Dyno/Vectorize.hs
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
-- these last nasty ones are for instance Vectorize f => Applicative/Additive/Metric/etc f
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE CPP #-}
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ <= 708
{-# LANGUAGE OverlappingInstances #-}
#endif
module Dyno.Vectorize
( Vectorize(..)
, devectorize
, None(..)
, Id(..)
, Tuple(..)
, Triple(..)
, vlength
, vzipWith
, vzipWith3
, vzipWith4
, vdiag
, vdiag'
, vnames
, vnames'
, GVectorize(..)
) where
import GHC.Generics
import Accessors ( Field, Lookup, accessors, flatten, flatten' )
import Control.Applicative
import Data.Either ( partitionEithers )
import Data.Serialize ( Serialize )
import qualified Data.Vector as V
import qualified Data.Foldable as F
import qualified Data.Traversable as T
import Data.Proxy ( Proxy(..) )
import qualified Linear
import SpatialMath ( Euler )
import SpatialMathT ( V3T, Rot )
import Text.Printf ( printf )
import Prelude -- BBP workaround
-- | a length-0 vectorizable type
data None a = None
deriving (Eq, Ord, Generic, Generic1, Functor, F.Foldable, T.Traversable, Show)
instance Vectorize None
instance Applicative None where
pure = const None
(<*>) = const (const None)
instance Linear.Additive None where
instance Serialize (None a)
-- | a length-1 vectorizable type
newtype Id a = Id { unId :: a }
deriving (Eq, Ord, Generic, Generic1, Functor, F.Foldable, T.Traversable, Show)
instance Vectorize Id
instance Applicative Id where
pure = Id
Id fx <*> Id x = Id (fx x)
instance Linear.Additive Id where
instance Serialize a => Serialize (Id a)
-- | a length-2 vectorizable type
data Tuple f g a = Tuple { unFst :: f a, unSnd :: g a }
deriving (Eq, Ord, Generic, Generic1, Functor, F.Foldable, T.Traversable, Show)
instance (Vectorize f, Vectorize g) => Vectorize (Tuple f g)
instance (Applicative f, Applicative g) => Applicative (Tuple f g) where
pure x = Tuple (pure x) (pure x)
Tuple fx fy <*> Tuple x y = Tuple (fx <*> x) (fy <*> y)
instance (Vectorize f, Vectorize g, Applicative f, Applicative g) => Linear.Additive (Tuple f g) where
zero = Tuple (fill 0) (fill 0)
-- | a length-3 vectorizable type
data Triple f g h a = Triple { unFst3 :: f a, unSnd3 :: g a, unThd3 :: h a }
deriving (Eq, Ord, Generic, Generic1, Functor, F.Foldable, T.Traversable, Show)
instance (Vectorize f, Vectorize g, Vectorize h) => Vectorize (Triple f g h)
instance (Applicative f, Applicative g, Applicative h) => Applicative (Triple f g h) where
pure x = Triple (pure x) (pure x) (pure x)
Triple fx fy fz <*> Triple x y z = Triple (fx <*> x) (fy <*> y) (fz <*> z)
instance (Vectorize f, Vectorize g, Vectorize h,
Applicative f, Applicative g, Applicative h)
=> Linear.Additive (Triple f g h) where
zero = Triple (fill 0) (fill 0) (fill 0)
instance Lookup (None a)
instance (Lookup a, Generic a) => Lookup (Id a)
instance (Lookup (f a), Generic (f a),
Lookup (g a), Generic (g a)) => Lookup (Tuple f g a)
instance (Lookup (f a), Generic (f a),
Lookup (g a), Generic (g a),
Lookup (h a), Generic (h a)) => Lookup (Triple f g h a)
instance Vectorize Linear.V0
instance Vectorize Linear.V1
instance Vectorize Linear.V2
instance Vectorize Linear.V3
instance Vectorize Linear.V4
instance Vectorize Linear.Quaternion
instance Vectorize Euler
instance Vectorize (V3T f)
instance Vectorize (Rot f1 f2)
-- | partial version of 'devectorize\'' which throws an error
-- if the vector length doesn' match the type length
devectorize :: Vectorize f => V.Vector a -> f a
devectorize x = case devectorize' x of
Right y -> y
Left msg -> error msg
vzipWith :: Vectorize f => (a -> b -> c) -> f a -> f b -> f c
vzipWith f x y = devectorize $ V.zipWith f (vectorize x) (vectorize y)
vzipWith3 :: Vectorize f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
vzipWith3 f x y z = devectorize $ V.zipWith3 f (vectorize x) (vectorize y) (vectorize z)
vzipWith4 :: Vectorize f => (a -> b -> c -> d -> e) -> f a -> f b -> f c -> f d -> f e
vzipWith4 f x y z w =
devectorize $ V.zipWith4 f (vectorize x) (vectorize y) (vectorize z) (vectorize w)
-- | Make a diagonal "matrix" from a "vector".
-- Off-diagonal elements will be 0, thus the Num constraint.
vdiag :: forall f a . (Vectorize f, Num a) => f a -> f (f a)
vdiag = flip vdiag' 0
-- | Make a diagonal "matrix" from a "vector" with a given off-diagonal value.
vdiag' :: forall f a . Vectorize f => f a -> a -> f (f a)
vdiag' v0 offDiag =
devectorize $ V.generate n (\k -> devectorize (V.generate n (\j -> gen j k)))
where
v = vectorize v0
n = vlength (Proxy :: Proxy f)
gen j k
| j /= k = offDiag
| otherwise = v V.! k
-- this could me more efficient as a class method, but this is safer
vlength :: Vectorize f => Proxy f -> Int
vlength = V.length . vectorize . (fill () `asFunctorOf`)
where
asFunctorOf :: f a -> Proxy f -> f a
asFunctorOf x _ = x
-- | fmap f == devectorize . (V.map f) . vectorize
class Functor f => Vectorize (f :: * -> *) where
vectorize :: f a -> V.Vector a
devectorize' :: V.Vector a -> Either String (f a)
fill :: a -> f a
default vectorize :: (Generic1 f, GVectorize (Rep1 f)) => f a -> V.Vector a
vectorize f = gvectorize (from1 f)
default devectorize' :: (Generic1 f, GVectorize (Rep1 f)) => V.Vector a -> Either String (f a)
devectorize' f = fmap to1 (gdevectorize f)
default fill :: (Generic1 f, GVectorize (Rep1 f)) => a -> f a
fill = to1 . gfill
-- undecidable, overlapping, orphan instances to get rid of boilerplate
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ <= 708
instance Vectorize f => Applicative f where
#else
instance {-# OVERLAPPABLE #-} Vectorize f => Applicative f where
#endif
pure = fill
x0 <*> x1 = devectorize (V.zipWith id (vectorize x0) (vectorize x1))
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ <= 708
instance Vectorize f => Linear.Additive f where
#else
instance {-# OVERLAPPABLE #-} Vectorize f => Linear.Additive f where
#endif
zero = fill 0
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ <= 708
instance Vectorize f => Linear.Metric f where
#else
instance {-# OVERLAPPABLE #-} Vectorize f => Linear.Metric f where
#endif
dot x0 x1 = V.sum $ V.zipWith (*) (vectorize x0) (vectorize x1)
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ <= 708
instance (Vectorize f, Eq a) => Eq (f a) where
#else
instance {-# OVERLAPPABLE #-} (Vectorize f, Eq a) => Eq (f a) where
#endif
x == y = (vectorize x) == (vectorize y)
x /= y = (vectorize x) /= (vectorize y)
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ <= 708
instance (Vectorize f, Ord a) => Ord (f a) where
#else
instance {-# OVERLAPPABLE #-} (Vectorize f, Ord a) => Ord (f a) where
#endif
compare x y = compare (vectorize x) (vectorize y)
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ <= 708
instance Vectorize f => F.Foldable f where
#else
instance {-# OVERLAPPABLE #-} Vectorize f => F.Foldable f where
#endif
foldMap f x = F.foldMap f (vectorize x)
foldr f acc0 x = F.foldr f acc0 (vectorize x)
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ <= 708
instance Vectorize f => T.Traversable f where
#else
instance {-# OVERLAPPABLE #-} Vectorize f => T.Traversable f where
#endif
traverse f x = devectorize <$> T.traverse f (vectorize x)
class GVectorize (f :: * -> *) where
gvectorize :: f a -> V.Vector a
gdevectorize :: V.Vector a -> Either String (f a)
gfill :: a -> f a
gvlength :: Proxy f -> Int
-- product type (concatination)
instance (GVectorize f, GVectorize g) => GVectorize (f :*: g) where
gvectorize (f :*: g) = gvectorize f V.++ gvectorize g
gdevectorize v0s
| V.length v0s < n0 =
Left $ "gdevectorize (f :*: g): V.length v0s < vlength f0 (" ++
show (V.length v0s) ++ " < " ++ show n0 ++ ")"
| V.length v1 /= n1 =
Left $ "gdevectorize (f :*: g): V.length v1 /= vlength f1 (" ++
show (V.length v1) ++ " /= " ++ show n1 ++ ")"
| otherwise = case (ef0, ef1) of
(Left msg0, Left msg1) ->
Left $ "gdevectorize (f :*: g): errored on both sides: {" ++ msg0 ++ ", " ++ msg1 ++ "}"
(Left msg0, Right _) ->
Left $ "gdevectorize (f :*: g): errored on left side: " ++ msg0
(Right _, Left msg1) ->
Left $ "gdevectorize (f :*: g): errored on right side: " ++ msg1
(Right f0, Right f1) -> Right (f0 :*: f1)
where
ef0 = gdevectorize v0
ef1 = gdevectorize v1
n0 = gvlength (Proxy :: Proxy f)
n1 = gvlength (Proxy :: Proxy g)
(v0,v1) = V.splitAt n0 v0s
gfill x = gfill x :*: gfill x
gvlength = const (nf + ng)
where
nf = gvlength (Proxy :: Proxy f)
ng = gvlength (Proxy :: Proxy g)
-- Metadata (constructor name, etc)
instance GVectorize f => GVectorize (M1 i c f) where
gvectorize = gvectorize . unM1
gdevectorize = fmap M1 . gdevectorize
gfill = M1 . gfill
gvlength = gvlength . proxy
where
proxy :: Proxy (M1 i c f) -> Proxy f
proxy = const Proxy
-- singleton
instance GVectorize Par1 where
gvectorize = V.singleton . unPar1
gdevectorize v = case V.toList v of
[] -> Left "gdevectorize Par1: got empty list"
[x] -> Right (Par1 x)
xs -> Left $ "gdevectorize Par1: got non-1 length: " ++ show (length xs)
gfill = Par1
gvlength = const 1
-- data with no fields
instance GVectorize U1 where
gvectorize = const V.empty
gdevectorize v
| V.null v = Right U1
| otherwise = Left $ "gdevectorize U1: got non-null vector, length: " ++ show (V.length v)
gfill = const U1
gvlength = const 0
-- Constants, additional parameters, and rank-1 recursion
instance Vectorize f => GVectorize (Rec1 f) where
gvectorize = vectorize . unRec1
gdevectorize = fmap Rec1 . devectorize'
gfill = Rec1 . fill
gvlength = vlength . proxy
where
proxy :: Proxy (Rec1 f) -> Proxy f
proxy = const Proxy
-- composition
instance (Vectorize f, GVectorize g) => GVectorize (f :.: g) where
gfill = Comp1 . devectorize'' . V.replicate k . gfill
where
devectorize'' x = case devectorize' x of
Right y -> y
Left msg -> error $ "gfill (f :.: g) devectorize error: " ++ msg
k = vlength (Proxy :: Proxy f)
gvectorize = V.concatMap gvectorize . vectorize . unComp1
gdevectorize v = case partitionEithers (V.toList evs) of
([], vs) -> fmap Comp1 (devectorize' (V.fromList vs))
(bad, good) -> Left $ printf "gdevectorize (f :.: g): got %d failures and %d successes"
(length bad) (length good)
where
kf = vlength (Proxy :: Proxy f)
kg = gvlength (Proxy :: Proxy g)
--evs :: V.Vector (Either String (g a))
evs = fmap gdevectorize (splitsAt kg kf v {-:: Vec nf (Vec ng a)-} )
gvlength = const (nf * ng)
where
nf = vlength (Proxy :: Proxy f)
ng = gvlength (Proxy :: Proxy g)
-- break a vector jOuter vectors, each of length kInner
splitsAt' :: Int -> Int -> V.Vector a -> [V.Vector a]
splitsAt' 0 jOuter v
| V.null v = replicate jOuter V.empty
| otherwise = error $ "splitsAt 0 " ++ show jOuter ++ ": got non-zero vector"
splitsAt' kInner 0 v
| V.null v = []
| otherwise = error $ "splitsAt " ++ show kInner ++ " 0: leftover vector of length: " ++ show (V.length v)
splitsAt' kInner jOuter v
| kv0 < kInner =
error $ "splitsAt " ++ show kInner ++ " " ++ show jOuter ++ ": " ++ "ran out of vector input"
| otherwise = v0 : splitsAt' kInner (jOuter - 1) v1
where
kv0 = V.length v0
(v0,v1) = V.splitAt kInner v
-- break a vector jOuter vectors, each of length kInner
splitsAt :: Int -> Int -> V.Vector a -> V.Vector (V.Vector a)
splitsAt k j = V.fromList . splitsAt' k j
-- | fill a vectorizable thing with its field names
vnames :: forall f . (Vectorize f, Lookup (f ())) => f String
vnames = case mr of
Left msg -> error $ "vnames devectorize error: " ++ msg
Right r -> r
where
mr = devectorize' $ V.fromList $
fmap fst (flatten accessors :: [(String, Field (f ()))])
-- | fill a vectorizable thing with its field name heirarchy
vnames' :: forall f . (Vectorize f, Lookup (f ())) => f [String]
vnames' = case mr of
Left msg -> error $ "vnames' devectorize error: " ++ msg
Right r -> r
where
mr = devectorize' $ V.fromList $
fmap fst (flatten' accessors :: [([String], Field (f ()))])