numerical-0.0.0.0: src/Numerical/Array/Pure.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ScopedTypeVariables#-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FunctionalDependencies #-}
module Numerical.Array.Pure where
--import Numerical.Array.Address
import qualified Numerical.Array.Layout as L
import Numerical.Array.Locality
import Numerical.Array.Shape
import Numerical.Array.Range
import Numerical.Array.Storage as S
import Numerical.World
import qualified Data.Vector.Generic as VG
{-
a general question that you might ask is "what primops need have a monad constraint"
ie rather than having type a -> b, why are they type Monad m => a -> m b ?
the answer boils down to the following: most array types have
a *PURE* header data structure that can't be mutated,
that contains the Shape, extent, some handle/pointer to the associated underlying
buffer/datastructure. Any (even nominally pure) access to that potentially
mutable buffer should be mediated by a monad.
I further assume that the *structure* and *extent* of this underlying buffer cannot change.
That is, A valid address will always stay valid, even if after some mutation it may
correspond to a *different* index than it did before.
-}
{-
Fix ME, these names are lame
ImmArray == immutable array
-}
data family ImmArray world rep lay (view::Locality) (rank :: Nat ) el
data instance ImmArray Native rep lay locality rank el =
ImMutableNativeArray {
nativeBufferPure :: ! (S.BufferPure rep el )
,nativeFormatPure :: ! (L.Format lay locality rank rep)
}
class PureArray arr (rank:: Nat) a | arr -> rank where
type PureArrayAddress (arr :: * -> * ) :: *
-- | gives the shape, a 'rank' length list of the dimensions
basicShape :: arr a -> Index rank
--basicUnsafeRead :: PrimMonad m => marr (PrimState m) a -> Shape rank Int -> m (Maybe a)
-- | basicMutableSparseIndexToAddres checks if a index is present or not
-- helpful primitive for authoring codes for (un)structured sparse array format
-- FIXME : THIS IS A TERRIBLE NAME
basicSparseIndexToAddress :: ( address ~PureArrayAddress arr) => arr a -> Index rank -> (Maybe address)
-- |
basicAddressToIndex :: (address ~PureArrayAddress arr) => arr a -> address -> (Index rank )
-- | return the Range of valid logical addresses
basicAddressRange :: (address ~PureArrayAddress arr)=> arr a -> Maybe (Range address)
-- | gives the next valid logical address
-- undefined on invalid addresses and the greatest valid address.
-- Note that for invalid addresses in between minAddress and maxAddress,
-- will return the next valid address
basicNextAddress :: (address ~PureArrayAddress arr)=> arr a -> address -> Maybe address
-- I think the case could be made for a basicPreviousAddress opeeration
-- | gives the next valid array index
-- undefined on invalid indices and the greatest valid index
basicNextIndex :: (address ~PureArrayAddress arr)=>
arr a -> Index rank -> Maybe address -> Maybe ( Index rank, address)
-- | for a given valid address, @'basicAddressRegion' addr @ will return an AddressInterval
-- that contains @addr@. This will be a singleton when the "maximal uniform stride interval"
-- containing @addr@ has strictly less than 3 elements. Otherwise will return an Address range
-- covering the maximal interval that will have cardinality at least 3.
--basicAddressRegion :: (address ~PureArrayAddress arr)=> arr a -> address -> UniformAddressInterval address
---- | Yield the element at the given position. This method should not be
---- called directly, use 'unsafeRead' instead.
basicUnsafeAddressRead :: (Monad m , address ~PureArrayAddress arr)=> arr a -> address-> m a
-- | Yield the element at the given position. This method should not be
-- called directly, use 'unsafeSparseRead' instead.
basicUnsafeSparseRead :: Monad m => arr a -> Index rank -> m (Maybe a)
-- the catch all layout instance
instance (Buffer rep el , L.Layout (L.Format lay locality rank rep) rank)
=>PureArray (ImmArray Native rep lay locality rank ) rank el where
type PureArrayAddress (ImmArray Native rep lay locality rank )
=L.LayoutAddress (L.Format lay locality rank rep)
{-# INLINE basicShape #-}
basicShape = L.basicLogicalShape . nativeFormatPure
{-# INLINE basicSparseIndexToAddress #-}
basicSparseIndexToAddress= L.basicToAddress . nativeFormatPure
{-# INLINE basicAddressToIndex #-}
basicAddressToIndex = L.basicToIndex . nativeFormatPure
{-# INLINE basicAddressRange #-}
basicAddressRange = L.basicAddressRange . nativeFormatPure
{-# INLINE basicNextAddress #-}
basicNextAddress= L.basicNextAddress . nativeFormatPure
{-# INLINE basicNextIndex #-}
basicNextIndex = L.basicNextIndex . nativeFormatPure
{-# INLINE basicUnsafeSparseRead #-}
basicUnsafeSparseRead =
\ arr ix -> case basicSparseIndexToAddress arr ix of
Nothing -> return Nothing
(Just addr) -> basicUnsafeAddressRead arr addr >>= ( return . Just)
{-# INLINE basicUnsafeAddressRead #-}
basicUnsafeAddressRead =
\ arr addr ->
VG.basicUnsafeIndexM (nativeBufferPure arr)
(L.basicAddressAsInt (nativeFormatPure arr) $ addr)
class PureArray arr rank a => PureDenseArray arr rank a where
-- | 'basicIndexInBounds' is an O(1) bounds check.
basicIndexInBounds :: arr a -> Index rank -> Bool
-- |
basicUnsafeAddressDenseRead :: (address ~PureArrayAddress arr,Monad m) => arr a -> address-> m a
-- | Yield the element at the given position. This method should not be
-- called directly, use 'unsafeRead' instead.
basicUnsafeDenseReadM :: Monad m => arr a -> Index rank -> m a