easytensor-1.0.0.0: src/Numeric/DataFrame/Type.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Numeric.DataFrame.Type
( -- * Data types
DataFrame (..)
, SomeDataFrame (..), DataFrame'
, pattern (:*:), pattern Z
-- * Infer type class instances
, AllTypes, ImplAllows, ArraySingletons, PrimFrames
, DataFrameInference (..)
, inferOrd, inferNum, inferFractional, inferFloating
, inferOrd', inferNum', inferFractional', inferFloating'
, inferASing', inferEq', inferShow', inferPrim', inferPrimElem'
-- * Misc
, ixOff, unsafeFromFlatList
, Dim1 (..), Dim2 (..), Dim3 (..)
, dimSize1, dimSize2, dimSize3
, bSizeOf, bAlignOf
-- * Re-exports from dimensions
, Dim (..), Idx (..), XNat (..), Dims, Idxs, TypedList (..)
) where
import Data.Proxy (Proxy)
import Foreign.Storable (Storable (..))
import GHC.Base
import GHC.Ptr (Ptr (..))
import Numeric.DataFrame.Family
import Numeric.DataFrame.Internal.Array.Class
import Numeric.DataFrame.Internal.Array.Family (Array, ArraySingleton (..))
import qualified Numeric.DataFrame.Internal.Array.Family as AFam
import Numeric.Dimensions
import Numeric.PrimBytes
-- | Single frame
newtype instance DataFrame (t :: Type) (ns :: [Nat])
= SingleFrame { _getDF :: Array t ns }
-- | Multiple "columns" of data frames of the same shape
newtype instance DataFrame (ts :: [Type]) (ns :: [Nat])
= MultiFrame { _getDFS :: TypedList (DataFrame' ns) ts }
-- | Data frame with some dimensions missing at compile time.
-- Pattern-match against its constructor to get a Nat-indexed data frame.
data instance DataFrame (ts :: l) (xns :: [XNat])
= forall (ns :: [Nat])
. (KnownXNatTypes xns, FixedDims xns ns, Dimensions ns, ArraySingletons ts ns)
=> XFrame (DataFrame ts ns)
-- | Data frame that has an unknown dimensionality at compile time.
-- Pattern-match against its constructor to get a Nat-indexed data frame
data SomeDataFrame (t :: l)
= forall (ns :: [Nat]) . (Dimensions ns, ArraySingletons t ns)
=> SomeDataFrame (DataFrame t ns)
-- | DataFrame with its type arguments swapped.
newtype DataFrame' (xs :: [k]) (t :: l) = DataFrame' (DataFrame t xs)
{-# COMPLETE Z, (:*:) #-}
-- | Constructing a @MultiFrame@ using DataFrame columns
pattern (:*:) :: forall (xs :: [Type]) (ns :: [Nat])
. ()
=> forall (y :: Type) (ys :: [Type])
. (xs ~ (y ': ys))
=> DataFrame y ns
-> DataFrame ys ns -> DataFrame xs ns
pattern (:*:) x xs <- (MultiFrame (DataFrame' x :* (MultiFrame -> xs)))
where
(:*:) x (MultiFrame xs) = MultiFrame (DataFrame' x :* xs)
infixr 6 :*:
-- | Empty MultiFrame
pattern Z :: forall (xs :: [Type]) (ns :: [Nat])
. () => (xs ~ '[]) => DataFrame xs ns
pattern Z = MultiFrame U
--------------------------------------------------------------------------------
-- All Eq instances
--------------------------------------------------------------------------------
deriving instance Eq (Array t ds) => Eq (DataFrame (t :: Type) (ds :: [Nat]))
instance ImplAllows Eq ts ds => Eq (DataFrame (ts :: [Type]) ds) where
Z == Z = True
(a :*: as) == (b :*: bs) = a == b && as == bs
instance (AllTypes Eq t, DataFrameInference t)
=> Eq (DataFrame (t :: l) (ds :: [XNat])) where
(XFrame dfa) == (XFrame dfb)
| Just E <- sameDims' dfa dfb
, E <- inferEq dfa = dfa == dfb
_ == _ = False
instance (AllTypes Eq t, DataFrameInference t)
=> Eq (SomeDataFrame (t :: l)) where
(SomeDataFrame dfa) == (SomeDataFrame dfb)
| Just E <- sameDims' dfa dfb
, E <- inferEq dfa = dfa == dfb
_ == _ = False
--------------------------------------------------------------------------------
-- All Show instances
--------------------------------------------------------------------------------
instance ( Show (Array t ds)
, Dimensions ds
) => Show (DataFrame (t :: Type) (ds :: [Nat])) where
show (SingleFrame arr) = unlines
[ "DF " ++ drop 5 (show $ dims @_ @ds) ++ ":"
, show arr
]
instance ( Dimensions ds
, ImplAllows Show ts ds
) => Show (DataFrame (ts :: [Type]) (ds :: [Nat])) where
show dfs = unlines $
("DF " ++ show (order dds) ++ " x " ++ drop 5 (show dds) ++ ":")
: showAll 1 dfs
where
dds = dims @_ @ds
showAll :: ImplAllows Show xs ds
=> Word -> DataFrame (xs :: [Type]) ds -> [String]
showAll _ Z = []
showAll n (SingleFrame arr :*: fs)
= ("Var " ++ show n) : show arr : showAll (n+1) fs
instance (AllTypes Show t, DataFrameInference t)
=> Show (DataFrame (t :: l) (xns :: [XNat])) where
show (XFrame df)
| E <- inferShow df = 'X': show df
instance (AllTypes Show t, DataFrameInference t)
=> Show (SomeDataFrame (t :: l)) where
show (SomeDataFrame df)
| E <- inferShow df = "Some" ++ show df
--------------------------------------------------------------------------------
type family AllTypes (f :: Type -> Constraint) (ts :: l) :: Constraint where
AllTypes f (t :: Type) = f t
AllTypes f (ts :: [Type]) = All f ts
type family ImplAllows (f :: Type -> Constraint) (ts :: l) (ds :: [Nat])
:: Constraint where
ImplAllows f (t :: Type) ds = f (Array t ds)
ImplAllows _ ('[] :: [Type]) _ = ()
ImplAllows f (t ': ts :: [Type]) ds = (f (Array t ds), ImplAllows f ts ds)
type family ArraySingletons (ts :: l) (ns :: [Nat]) :: Constraint where
ArraySingletons (t :: Type) ns = ArraySingleton t ns
ArraySingletons ('[] :: [Type]) _ = ()
ArraySingletons (t ': ts :: [Type]) ns
= (ArraySingleton t ns, ArraySingletons ts ns)
type family PrimFrames (ts :: l) (ns :: [Nat]) :: Constraint where
PrimFrames (t :: Type) ns
= (PrimBytes (DataFrame t ns), PrimArray t (DataFrame t ns))
PrimFrames ('[] :: [Type]) _ = ()
PrimFrames (t ': ts :: [Type]) ns
= ( PrimBytes (DataFrame t ns), PrimArray t (DataFrame t ns)
, PrimFrames ts ns)
deriving instance Bounded (Array t ds) => Bounded (DataFrame t ds)
deriving instance Enum (Array t ds) => Enum (DataFrame t ds)
deriving instance Integral (Array t ds)
=> Integral (DataFrame t ds)
deriving instance Num (Array t ds)
=> Num (DataFrame t ds)
deriving instance Fractional (Array t ds)
=> Fractional (DataFrame t ds)
deriving instance Floating (Array t ds)
=> Floating (DataFrame t ds)
deriving instance Ord (Array t ds)
=> Ord (DataFrame t ds)
deriving instance ( Read (Array t ds), Dimensions ds )
=> Read (DataFrame t ds)
deriving instance Real (Array t ds)
=> Real (DataFrame t ds)
deriving instance RealFrac (Array t ds)
=> RealFrac (DataFrame t ds)
deriving instance RealFloat (Array t ds)
=> RealFloat (DataFrame t ds)
deriving instance (PrimArray t (Array t ds), PrimBytes t)
=> PrimArray t (DataFrame t ds)
deriving instance PrimBytes (Array t ds)
=> PrimBytes (DataFrame t ds)
instance PrimBytes (DataFrame t ds) => Storable (DataFrame t ds) where
sizeOf x = I# (byteSize x)
alignment x = I# (byteAlign x)
peek (Ptr addr) = IO (readAddr addr)
poke (Ptr addr) a = IO (\s -> (# writeAddr a addr s, () #))
class DataFrameInference (t :: l) where
-- | Bring an evidence of `ArraySingleton` instance into
-- a scope at runtime.
-- This is often used to let GHC infer other complex type class instances,
-- such as `SubSpace`.
inferASing
:: (AllTypes PrimBytes t, Dimensions ds)
=> DataFrame t ds -> Evidence (ArraySingletons t ds)
inferEq
:: (AllTypes Eq t, ArraySingletons t ds)
=> DataFrame t ds -> Evidence (Eq (DataFrame t ds))
inferShow
:: (AllTypes Show t, ArraySingletons t ds, Dimensions ds)
=> DataFrame t ds -> Evidence (Show (DataFrame t ds))
inferPrim
:: (AllTypes PrimBytes t, ArraySingletons t ds, Dimensions ds)
=> DataFrame t ds -> Evidence (PrimFrames t ds)
-- | This is a special function, because Scalar does not require PrimBytes.
-- That is why the dimension list in the argument nust not be empty.
inferPrimElem
:: (ArraySingletons t ds, ds ~ (Head ds ': Tail ds))
=> DataFrame t ds -> Evidence (AllTypes PrimBytes t)
instance DataFrameInference (t :: Type) where
inferASing (_ :: DataFrame t ds)
= AFam.inferASing @t @ds
inferEq (_ :: DataFrame t ds)
= case AFam.inferEq @t @ds of E -> E
inferShow (_ :: DataFrame t ds)
= case AFam.inferShow @t @ds of E -> E
inferPrim (_ :: DataFrame t ds)
= case AFam.inferPrim @t @ds of E -> E
inferPrimElem (_ :: DataFrame t ds)
= case AFam.inferPrimElem @t @(Head ds) @(Tail ds) of E -> E
inferOrd' :: forall t ds
. (Ord t, ArraySingleton t ds)
=> Evidence (Ord (DataFrame t ds))
inferOrd' = case AFam.inferOrd @t @ds of E -> E
inferNum' :: forall t ds
. (Num t, ArraySingletons t ds)
=> Evidence (Num (DataFrame t ds))
inferNum' = case AFam.inferNum @t @ds of E -> E
inferFractional' :: forall t ds
. (Fractional t, ArraySingleton t ds)
=> Evidence (Fractional (DataFrame t ds))
inferFractional' = case AFam.inferFractional @t @ds of E -> E
inferFloating' :: forall t ds
. (Floating t, ArraySingleton t ds)
=> Evidence (Floating (DataFrame t ds))
inferFloating' = case AFam.inferFloating @t @ds of E -> E
instance RepresentableList ts => DataFrameInference (ts :: [Type]) where
inferASing (_ :: DataFrame t ds)
= inferASings @ts @ds (tList @_ @ts)
inferEq (_ :: DataFrame t ds)
= case inferEqs @ts @ds (tList @_ @ts) of E -> E
inferShow (_ :: DataFrame t ds)
= case inferShows @ts @ds (tList @_ @ts) of E -> E
inferPrim (_ :: DataFrame t ds)
= case inferPrims @ts @ds (tList @_ @ts) of E -> E
inferPrimElem (_ :: DataFrame t ds)
= case inferPrimElems @ts @(Head ds) @(Tail ds) (tList @_ @ts) of E -> E
inferASings :: forall ts ds
. (All PrimBytes ts, Dimensions ds)
=> TypeList ts -> Evidence (ArraySingletons ts ds)
inferASings U = E
inferASings ((_ :: Proxy t) :* ts)
= case (inferASing' @t @ds, inferASings @_ @ds ts) of (E, E) -> E
inferEqs :: forall ts ds
. (All Eq ts, ArraySingletons ts ds)
=> TypeList ts -> Evidence (ImplAllows Eq ts ds)
inferEqs U = E
inferEqs ((_ :: Proxy t) :* ts)
= case (AFam.inferEq @t @ds, inferEqs @_ @ds ts) of (E, E) -> E
inferShows :: forall ts ds
. (All Show ts, ArraySingletons ts ds, Dimensions ds)
=> TypeList ts -> Evidence (ImplAllows Show ts ds)
inferShows U = E
inferShows ((_ :: Proxy t) :* ts)
= case (AFam.inferShow @t @ds, inferShows @_ @ds ts) of (E, E) -> E
inferPrims :: forall ts ds
. (All PrimBytes ts, ArraySingletons ts ds, Dimensions ds)
=> TypeList ts -> Evidence (PrimFrames ts ds)
inferPrims U = E
inferPrims ((_ :: Proxy t) :* ts)
= case (AFam.inferPrim @t @ds, inferPrims @_ @ds ts) of (E, E) -> E
inferPrimElems :: forall ts d ds
. (ArraySingletons ts (d ': ds))
=> TypeList ts -> Evidence (All PrimBytes ts)
inferPrimElems U = E
inferPrimElems ((_ :: Proxy t) :* ts)
= case (AFam.inferPrimElem @t @d @ds, inferPrimElems @_ @d @ds ts) of (E, E) -> E
inferASing' :: forall t ds
. (DataFrameInference t, AllTypes PrimBytes t, Dimensions ds)
=> Evidence (ArraySingletons t ds)
inferASing' = inferASing (undefined :: DataFrame t ds)
inferEq' :: forall t ds
. (DataFrameInference t, AllTypes Eq t, ArraySingletons t ds)
=> Evidence (Eq (DataFrame t ds))
inferEq' = inferEq (undefined :: DataFrame t ds)
inferShow' :: forall t ds
. ( DataFrameInference t, AllTypes Show t
, ArraySingletons t ds, Dimensions ds)
=> Evidence (Show (DataFrame t ds))
inferShow' = inferShow (undefined :: DataFrame t ds)
inferPrim' :: forall t ds
. ( DataFrameInference t, AllTypes PrimBytes t
, ArraySingletons t ds, Dimensions ds)
=> Evidence (PrimFrames t ds)
inferPrim' = inferPrim (undefined :: DataFrame t ds)
inferPrimElem' :: forall t ds
. ( DataFrameInference t, ArraySingletons t ds
, ds ~ (Head ds ': Tail ds))
=> Evidence (AllTypes PrimBytes t)
inferPrimElem' = inferPrimElem (undefined :: DataFrame t ds)
inferOrd :: forall t ds
. (Ord t, ArraySingleton t ds)
=> DataFrame t ds -> Evidence (Ord (DataFrame t ds))
inferOrd = const (inferOrd' @t @ds)
inferNum :: forall t ds
. (Num t, ArraySingletons t ds)
=> DataFrame t ds -> Evidence (Num (DataFrame t ds))
inferNum = const (inferNum' @t @ds)
inferFractional :: forall t ds
. (Fractional t, ArraySingleton t ds)
=> DataFrame t ds -> Evidence (Fractional (DataFrame t ds))
inferFractional = const (inferFractional' @t @ds)
inferFloating :: forall t ds
. (Floating t, ArraySingleton t ds)
=> DataFrame t ds -> Evidence (Floating (DataFrame t ds))
inferFloating = const (inferFloating' @t @ds)
--------------------------------------------------------------------------------
-- * Misc
--------------------------------------------------------------------------------
-- | A wrapper on `byteSize`
bSizeOf :: PrimBytes a => a -> Int
bSizeOf a = I# (byteSize a)
-- | A wrapper on `byteAlign`
bAlignOf :: PrimBytes a => a -> Int
bAlignOf a = I# (byteAlign a)
-- | Number of elements along the 1st dimension.
dimSize1 :: Dim1 t ds => t ds -> Word
dimSize1 = dimVal . dim1
-- | Number of elements along the 2nd dimension.
dimSize2 :: Dim2 t ds => t ds -> Word
dimSize2 = dimVal . dim2
-- | Number of elements along the 3rd dimension.
dimSize3 :: Dim3 t ds => t ds -> Word
dimSize3 = dimVal . dim3
class Dim1 (t :: [k] -> Type) (ds :: [k]) where
dim1 :: t ds -> Dim (Head ds)
class Dim2 (t :: [k] -> Type) (ds :: [k]) where
dim2 :: t ds -> Dim (Head (Tail ds))
class Dim3 (t :: [k] -> Type) (ds :: [k]) where
dim3 :: t ds -> Dim (Head (Tail (Tail ds)))
instance {-# OVERLAPPABLE #-}
Dimensions (d ': ds)
=> Dim1 t (d ': ds :: [k]) where
dim1 _ = case dims @k @(d ': ds) of d :* _ -> d
instance {-# OVERLAPPING #-}
Dim1 (TypedList Dim) (d ': ds) where
dim1 (d :* _) = d
instance {-# OVERLAPPING #-}
Dim1 (DataFrame l) (d ': ds :: [XNat]) where
dim1 (XFrame (_ :: DataFrame l ns))
= case xDims' @(d ': ds) @ns of d :* _ -> d
instance {-# OVERLAPPABLE #-}
Dimensions (d1 ': d2 ': ds)
=> Dim2 t (d1 ': d2 ': ds :: [k]) where
dim2 _ = case dims @k @(d1 ': d2 ': ds) of _ :* d :* _ -> d
instance {-# OVERLAPPING #-}
Dim2 (TypedList Dim) (d1 ': d2 ': ds) where
dim2 (_ :* d :* _) = d
instance {-# OVERLAPPING #-}
Dim2 (DataFrame l) (d1 ': d2 ': ds :: [XNat]) where
dim2 (XFrame (_ :: DataFrame l ns))
= case xDims' @(d1 ': d2 ': ds) @ns of _ :* d :* _ -> d
instance {-# OVERLAPPABLE #-}
Dimensions (d1 ': d2 ': d3 ': ds)
=> Dim3 t (d1 ': d2 ': d3 ': ds :: [k]) where
dim3 _ = case dims @k @(d1 ': d2 ': d3 ': ds) of _ :* _ :* d :* _ -> d
instance {-# OVERLAPPING #-}
Dim3 (TypedList Dim) (d1 ': d2 ': d3 ': ds) where
dim3 (_ :* _ :* d :* _) = d
instance {-# OVERLAPPING #-}
Dim3 (DataFrame l) (d1 ': d2 ': d3 ': ds :: [XNat]) where
dim3 (XFrame (_ :: DataFrame l ns))
= case xDims' @(d1 ': d2 ': d3 ': ds) @ns of _ :* _ :* d :* _ -> d