harpie-0.1.3.0: Haskell array programming.
Safe HaskellNone
LanguageGHC2021

Harpie.Array

Description

Arrays with shape information and computations at a value-level.

Synopsis

Usage

Several names used in harpie conflict with Prelude:

>>> import Prelude hiding (cycle, repeat, take, drop, zipWith, length)

In general, Array functionality is contained in Harpie.Array and shape functionality is contained in Harpie.Shape. These two modules also have name clashes and at least one needs to be qualified:

>>> import Harpie.Array as A
>>> import Harpie.Shape qualified as S

prettyprinter is used to prettily render arrays to better visualise shape.

>>> import Prettyprinter hiding (dot,fill)

Examples of arrays:

An array with no dimensions (a scalar).

>>> s = 1 :: Array Int
>>> s
UnsafeArray [] [1]
>>> shape s
[]
>>> pretty s
1

A single-dimension array (a vector).

>>> let v = range [3]
>>> pretty v
[0,1,2]

A two-dimensional array (a matrix).

>>> let m = range [2,3]
>>> pretty m
[[0,1,2],
 [3,4,5]]

An n-dimensional array (n should be finite).

>>> a = range [2,3,4]
>>> a
UnsafeArray [2,3,4] [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]
>>> pretty a
[[[0,1,2,3],
  [4,5,6,7],
  [8,9,10,11]],
 [[12,13,14,15],
  [16,17,18,19],
  [20,21,22,23]]]

Harpie Arrays

data Array a Source #

A hyperrectangular (or multidimensional) array with a value-level shape.

>>> let a = array [2,3,4] [1..24] :: Array Int
>>> a
UnsafeArray [2,3,4] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]
>>> pretty a
[[[1,2,3,4],
  [5,6,7,8],
  [9,10,11,12]],
 [[13,14,15,16],
  [17,18,19,20],
  [21,22,23,24]]]

Constructors

UnsafeArray [Int] (Vector a) 

Instances

Instances details
Foldable Array Source # 
Instance details

Defined in Harpie.Array

Methods

fold :: Monoid m => Array m -> m #

foldMap :: Monoid m => (a -> m) -> Array a -> m #

foldMap' :: Monoid m => (a -> m) -> Array a -> m #

foldr :: (a -> b -> b) -> b -> Array a -> b #

foldr' :: (a -> b -> b) -> b -> Array a -> b #

foldl :: (b -> a -> b) -> b -> Array a -> b #

foldl' :: (b -> a -> b) -> b -> Array a -> b #

foldr1 :: (a -> a -> a) -> Array a -> a #

foldl1 :: (a -> a -> a) -> Array a -> a #

toList :: Array a -> [a] #

null :: Array a -> Bool #

length :: Array a -> Int #

elem :: Eq a => a -> Array a -> Bool #

maximum :: Ord a => Array a -> a #

minimum :: Ord a => Array a -> a #

sum :: Num a => Array a -> a #

product :: Num a => Array a -> a #

Traversable Array Source # 
Instance details

Defined in Harpie.Array

Methods

traverse :: Applicative f => (a -> f b) -> Array a -> f (Array b) #

sequenceA :: Applicative f => Array (f a) -> f (Array a) #

mapM :: Monad m => (a -> m b) -> Array a -> m (Array b) #

sequence :: Monad m => Array (m a) -> m (Array a) #

Functor Array Source # 
Instance details

Defined in Harpie.Array

Methods

fmap :: (a -> b) -> Array a -> Array b #

(<$) :: a -> Array b -> Array a #

Generic (Array a) Source # 
Instance details

Defined in Harpie.Array

Associated Types

type Rep (Array a) 
Instance details

Defined in Harpie.Array

type Rep (Array a) = D1 ('MetaData "Array" "Harpie.Array" "harpie-0.1.3.0-8crv803hRbwGkw3hfzzgTt" 'False) (C1 ('MetaCons "UnsafeArray" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 [Int]) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Vector a))))

Methods

from :: Array a -> Rep (Array a) x #

to :: Rep (Array a) x -> Array a #

Num a => Num (Array a) Source # 
Instance details

Defined in Harpie.Array

Methods

(+) :: Array a -> Array a -> Array a #

(-) :: Array a -> Array a -> Array a #

(*) :: Array a -> Array a -> Array a #

negate :: Array a -> Array a #

abs :: Array a -> Array a #

signum :: Array a -> Array a #

fromInteger :: Integer -> Array a #

Show a => Show (Array a) Source # 
Instance details

Defined in Harpie.Array

Methods

showsPrec :: Int -> Array a -> ShowS #

show :: Array a -> String #

showList :: [Array a] -> ShowS #

Eq a => Eq (Array a) Source # 
Instance details

Defined in Harpie.Array

Methods

(==) :: Array a -> Array a -> Bool #

(/=) :: Array a -> Array a -> Bool #

Ord a => Ord (Array a) Source # 
Instance details

Defined in Harpie.Array

Methods

compare :: Array a -> Array a -> Ordering #

(<) :: Array a -> Array a -> Bool #

(<=) :: Array a -> Array a -> Bool #

(>) :: Array a -> Array a -> Bool #

(>=) :: Array a -> Array a -> Bool #

max :: Array a -> Array a -> Array a #

min :: Array a -> Array a -> Array a #

Show a => Pretty (Array a) Source # 
Instance details

Defined in Harpie.Array

Methods

pretty :: Array a -> Doc ann #

prettyList :: [Array a] -> Doc ann #

FromArray (Array a) a Source # 
Instance details

Defined in Harpie.Array

Methods

asArray :: Array a -> Array a Source #

arrayAs :: Array a -> Array a Source #

FromVector (Array a) a Source # 
Instance details

Defined in Harpie.Array

Methods

asVector :: Array a -> Vector a Source #

vectorAs :: Vector a -> Array a Source #

type Rep (Array a) Source # 
Instance details

Defined in Harpie.Array

type Rep (Array a) = D1 ('MetaData "Array" "Harpie.Array" "harpie-0.1.3.0-8crv803hRbwGkw3hfzzgTt" 'False) (C1 ('MetaCons "UnsafeArray" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 [Int]) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Vector a))))

array :: FromVector t a => [Int] -> t -> Array a Source #

Construct an array from a shape and a value without any shape validation.

>>> array [2,3] [0..5]
UnsafeArray [2,3] [0,1,2,3,4,5]

(><) :: FromVector t a => [Int] -> t -> Array a infixl 4 Source #

Construct an Array.

>>> pretty $ [2,3] >< [0..5]
[[0,1,2],
 [3,4,5]]

validate :: Array a -> Bool Source #

Validate the size and shape of an array.

>>> validate (array [2,3,4] [1..23] :: Array Int)
False

safeArray :: FromVector t a => [Int] -> t -> Maybe (Array a) Source #

Construct an Array, checking shape.

>>> safeArray [2,3,4] [0..23] == Just a
True

unsafeModifyShape :: ([Int] -> [Int]) -> Array a -> Array a Source #

Unsafely modify an array shape.

>>> unsafeModifyShape (fmap (+1) :: [Int] -> [Int]) (array [2,3] [0..5])
UnsafeArray [3,4] [0,1,2,3,4,5]

unsafeModifyVector :: (FromVector u a, FromVector v b) => (u -> v) -> Array a -> Array b Source #

Unsafely modify an array vector.

>>> unsafeModifyVector (V.map (+1)) (array [2,3] [0..5])
UnsafeArray [2,3] [1,2,3,4,5,6]

Dimensions

type Dim = Int Source #

Representation of an index into a shape (an [Int]). The index is a dimension of the shape.

type Dims = [Int] Source #

Representation of indexes into a shape (an [Int]). The indexes are dimensions of the shape.

Conversion

class FromVector t a | t -> a where Source #

Conversion to and from a Vector

Note that conversion of an Array to a vector drops shape information, so that:

vectorAs . asVector == id
asVector . vectorAs == flat
>>> asVector (range [2,3])
[0,1,2,3,4,5]
>>> vectorAs (V.fromList [0..5]) :: Array Int
UnsafeArray [6] [0,1,2,3,4,5]

Methods

asVector :: t -> Vector a Source #

vectorAs :: Vector a -> t Source #

Instances

Instances details
FromVector (Array a) a Source # 
Instance details

Defined in Harpie.Array

Methods

asVector :: Array a -> Vector a Source #

vectorAs :: Vector a -> Array a Source #

FromVector (Vector a) a Source # 
Instance details

Defined in Harpie.Array

Methods

asVector :: Vector a -> Vector a Source #

vectorAs :: Vector a -> Vector a Source #

FromVector [a] a Source # 
Instance details

Defined in Harpie.Array

Methods

asVector :: [a] -> Vector a Source #

vectorAs :: Vector a -> [a] Source #

class FromArray t a | t -> a where Source #

Conversion to and from an Array

Note that conversion of an Array to a FromArray likely drops shape information, so that:

arrayAs . asArray == flat
asArray . arrayAs == id
>>> asArray ([0..5::Int])
UnsafeArray [6] [0,1,2,3,4,5]
>>> arrayAs (range [2,3]) :: [Int]
[0,1,2,3,4,5]

Methods

asArray :: t -> Array a Source #

arrayAs :: Array a -> t Source #

Instances

Instances details
FromArray (Array a) a Source # 
Instance details

Defined in Harpie.Array

Methods

asArray :: Array a -> Array a Source #

arrayAs :: Array a -> Array a Source #

FromArray (Vector a) a Source # 
Instance details

Defined in Harpie.Array

Methods

asArray :: Vector a -> Array a Source #

arrayAs :: Array a -> Vector a Source #

FromArray [a] a Source # 
Instance details

Defined in Harpie.Array

Methods

asArray :: [a] -> Array a Source #

arrayAs :: Array a -> [a] Source #

Shape Access

shape :: Array a -> [Int] Source #

shape of an Array

>>> shape a
[2,3,4]

rank :: Array a -> Int Source #

rank of an Array

>>> rank a
3

size :: Array a -> Int Source #

size of an Array, which is the total number of elements, if the Array is valid.

>>> size a
24

length :: Array a -> Int Source #

Number of rows (first dimension size) in an Array. As a convention, a scalar value is still a single row.

>>> length a
2
>>> length (toScalar 0)
1

isNull :: Array a -> Bool Source #

Is the Array empty (has zero number of elements).

>>> isNull ([2,0] >< [] :: Array ())
True
>>> isNull ([] >< [4] :: Array Int)
False

Indexing

index :: Array a -> [Int] -> a Source #

Extract an element at an index, unsafely.

>>> index a [1,2,3]
23

(!) :: Array a -> [Int] -> a infixl 9 Source #

Extract an element at an index, unsafely.

>>> a ! [1,2,3]
23

(!?) :: Array a -> [Int] -> Maybe a Source #

Extract an element at an index, safely.

>>> a !? [1,2,3]
Just 23
>>> a !? [2,3,1]
Nothing

tabulate :: [Int] -> ([Int] -> a) -> Array a Source #

Tabulate an array supplying a shape and a tabulation function.

>>> tabulate [2,3,4] (S.flatten [2,3,4]) == a
True

backpermute :: ([Int] -> [Int]) -> ([Int] -> [Int]) -> Array a -> Array a Source #

backpermute is a tabulation where the contents of an array do not need to be accessed, and is thus a fulcrum for leveraging laziness and fusion via the rule:

backpermute f g (backpermute f' g' a) == backpermute (f . f') (g . g') a

Many functions in this module are examples of backpermute usage.

>>> pretty $ backpermute List.reverse List.reverse a
[[[0,12],
  [4,16],
  [8,20]],
 [[1,13],
  [5,17],
  [9,21]],
 [[2,14],
  [6,18],
  [10,22]],
 [[3,15],
  [7,19],
  [11,23]]]

Scalars

fromScalar :: Array a -> a Source #

Unwrap a scalar.

>>> let s = array [] [3] :: Array Int
>>> fromScalar s
3

toScalar :: a -> Array a Source #

Wrap a scalar.

>>> :t toScalar 2
toScalar 2 :: Num a => Array a

isScalar :: Array a -> Bool Source #

Is an array a Scalar?

>>> isScalar (toScalar (2::Int))
True

asSingleton :: Array a -> Array a Source #

Convert a scalar to being a dimensioned array. Do nothing if not a scalar.

>>> asSingleton (toScalar 4)
UnsafeArray [1] [4]

asScalar :: Array a -> Array a Source #

Convert an array with shape [1] to being a scalar (Do nothing if not a shape [1] array).

>>> asScalar (singleton 3)
UnsafeArray [] [3]

Array Creation

empty :: Array a Source #

An array with no elements.

>>> empty
UnsafeArray [0] []

range :: [Int] -> Array Int Source #

An enumeration of row-major or lexicographic order.

>>> pretty $ range [2,3]
[[0,1,2],
 [3,4,5]]

corange :: [Int] -> Array Int Source #

An enumeration of col-major or colexicographic order.

>>> pretty (corange [2,3,4])
[[[0,6,12,18],
  [2,8,14,20],
  [4,10,16,22]],
 [[1,7,13,19],
  [3,9,15,21],
  [5,11,17,23]]]

indices :: [Int] -> Array [Int] Source #

Indices of an array shape.

>>> pretty $ indices [3,3]
[[[0,0],[0,1],[0,2]],
 [[1,0],[1,1],[1,2]],
 [[2,0],[2,1],[2,2]]]

ident :: Num a => [Int] -> Array a Source #

The identity array.

>>> pretty $ ident [3,3]
[[1,0,0],
 [0,1,0],
 [0,0,1]]

konst :: [Int] -> a -> Array a Source #

Create an array composed of a single value.

>>> pretty $ konst [3,2] 1
[[1,1],
 [1,1],
 [1,1]]

singleton :: a -> Array a Source #

Create an array of shape [1].

>>> pretty $ singleton 1
[1]
>>> singleton 3 == toScalar 3
False
>>> asVector (singleton 3) == asVector (toScalar 3)
True

diag :: Array a -> Array a Source #

Extract the diagonal of an array.

>>> pretty $ diag (ident [3,3])
[1,1,1]

undiag :: Num a => Array a -> Array a Source #

Expand the array to form a diagonal array.

>>> pretty $ undiag (range [3])
[[0,0,0],
 [0,1,0],
 [0,0,2]]

Element-level functions

zipWith :: (a -> b -> c) -> Array a -> Array b -> Array c Source #

Zip two arrays at an element level.

>>> zipWith (-) v v
UnsafeArray [3] [0,0,0]

zipWithSafe :: (a -> b -> c) -> Array a -> Array b -> Maybe (Array c) Source #

Zip two arrays at an element level, checking for shape consistency.

>>> zipWithSafe (-) (range [3]) (range [4])
Nothing

modify :: [Int] -> (a -> a) -> Array a -> Array a Source #

Modify a single value at an index.

>>> pretty $ modify [0,0] (const 100) (range [3,2])
[[100,1],
 [2,3],
 [4,5]]

imap :: ([Int] -> a -> b) -> Array a -> Array b Source #

Maps an index function at element-level.

>>> pretty $ imap (\xs x -> x - sum xs) a
[[[0,0,0,0],
  [3,3,3,3],
  [6,6,6,6]],
 [[11,11,11,11],
  [14,14,14,14],
  [17,17,17,17]]]

Function generalisers

rowWise :: (Dims -> [x] -> Array a -> Array a) -> [x] -> Array a -> Array a Source #

With a function that takes dimensions and (type-level) parameters, apply the parameters to the initial dimensions. ie

rowWise f xs = f [0..] xs
>>> rowWise indexes [1,0] a
UnsafeArray [4] [12,13,14,15]

colWise :: (Dims -> [x] -> Array a -> Array a) -> [x] -> Array a -> Array a Source #

With a function that takes dimensions and (type-level) parameters, apply the parameters to the the last dimensions. ie

colWise f xs = f (List.reverse [0 .. (rank a - 1)]) xs
>>> colWise indexes [1,0] a
UnsafeArray [2] [1,13]

dimsWise :: (Dim -> x -> Array a -> Array a) -> Dims -> [x] -> Array a -> Array a Source #

With a function that takes a dimension and a parameter, fold dimensions and parameters using the function.

>>> dimsWise take [0,2] [1,2] a
UnsafeArray [1,3,2] [0,1,4,5,8,9]

Single-dimension functions

take :: Dim -> Int -> Array a -> Array a Source #

Take the top-most elements across the specified dimension. Negative values take the bottom-most. No index check is performed.

take d x == takes [(d,x)]
>>> pretty $ take 2 1 a
[[[0],
  [4],
  [8]],
 [[12],
  [16],
  [20]]]
>>> pretty $ take 2 (-1) a
[[[3],
  [7],
  [11]],
 [[15],
  [19],
  [23]]]

drop :: Dim -> Int -> Array a -> Array a Source #

Drop the top-most elements across the specified dimension. Negative values take the bottom-most.

>>> pretty $ drop 2 1 a
[[[1,2,3],
  [5,6,7],
  [9,10,11]],
 [[13,14,15],
  [17,18,19],
  [21,22,23]]]
>>> pretty $ drop 2 (-1) a
[[[0,1,2],
  [4,5,6],
  [8,9,10]],
 [[12,13,14],
  [16,17,18],
  [20,21,22]]]

select :: Dim -> Int -> Array a -> Array a Source #

Select an index along a dimension.

>>> let s = select 2 3 a
>>> pretty s
[[3,7,11],
 [15,19,23]]

insert :: Dim -> Int -> Array a -> Array a -> Array a Source #

Insert along a dimension at a position.

>>> pretty $ insert 2 0 a (konst [2,3] 0)
[[[0,0,1,2,3],
  [0,4,5,6,7],
  [0,8,9,10,11]],
 [[0,12,13,14,15],
  [0,16,17,18,19],
  [0,20,21,22,23]]]
>>> insert 0 0 (toScalar 1) (toScalar 2)
UnsafeArray [2] [2,1]

delete :: Dim -> Int -> Array a -> Array a Source #

Delete along a dimension at a position.

>>> pretty $ delete 2 0 a
[[[1,2,3],
  [5,6,7],
  [9,10,11]],
 [[13,14,15],
  [17,18,19],
  [21,22,23]]]

append :: Dim -> Array a -> Array a -> Array a Source #

Insert along a dimension at the end.

>>> pretty $ append 2 a (konst [2,3] 0)
[[[0,1,2,3,0],
  [4,5,6,7,0],
  [8,9,10,11,0]],
 [[12,13,14,15,0],
  [16,17,18,19,0],
  [20,21,22,23,0]]]

prepend :: Dim -> Array a -> Array a -> Array a Source #

Insert along a dimension at the beginning.

>>> pretty $ prepend 2 (konst [2,3] 0) a
[[[0,0,1,2,3],
  [0,4,5,6,7],
  [0,8,9,10,11]],
 [[0,12,13,14,15],
  [0,16,17,18,19],
  [0,20,21,22,23]]]

concatenate :: Dim -> Array a -> Array a -> Array a Source #

Concatenate along a dimension.

>>> shape $ concatenate 1 a a
[2,6,4]
>>> concatenate 0 (toScalar 1) (toScalar 2)
UnsafeArray [2] [1,2]
>>> concatenate 0 (toScalar 0) (asArray [1..3])
UnsafeArray [4] [0,1,2,3]

couple :: Int -> Array a -> Array a -> Array a Source #

Combine two arrays as a new dimension of a new array.

>>> pretty $ couple 0 (asArray [1,2,3]) (asArray [4,5,6::Int])
[[1,2,3],
 [4,5,6]]

slice :: Dim -> Int -> Int -> Array a -> Array a Source #

Slice along a dimension with the supplied offset & length.

>>> let s = slice 2 1 2 a
>>> pretty s
[[[1,2],
  [5,6],
  [9,10]],
 [[13,14],
  [17,18],
  [21,22]]]

rotate :: Dim -> Int -> Array a -> Array a Source #

Rotate an array along a dimension.

>>> pretty $ rotate 1 2 a
[[[8,9,10,11],
  [0,1,2,3],
  [4,5,6,7]],
 [[20,21,22,23],
  [12,13,14,15],
  [16,17,18,19]]]

Multi-dimension functions

takes :: Dims -> [Int] -> Array a -> Array a Source #

Takes the top-most elements across the supplied dimension,n tuples. Negative values take the bottom-most.

takes == dimsWise take
>>> pretty $ takes [0,2] [1,-3] a
[[[1,2,3],
  [5,6,7],
  [9,10,11]]]

drops :: Dims -> [Int] -> Array a -> Array a Source #

Drops the top-most elements. Negative values drop the bottom-most.

>>> pretty $ drops [0,1,2] [1,2,-3] a
[[[20]]]

indexes :: Dims -> [Int] -> Array a -> Array a Source #

Select by dimensions and indexes.

>>> let s = indexes [0,1] [1,1] a
>>> pretty s
[16,17,18,19]

slices :: Dims -> [Int] -> [Int] -> Array a -> Array a Source #

Slice along dimensions with the supplied offsets and lengths.

>>> let s = slices [2,0] [1,1] [2,1] a
>>> pretty s
[[[13,14],
  [17,18],
  [21,22]]]

heads :: Dims -> Array a -> Array a Source #

Select the first element along the supplied dimensions.

>>> pretty $ heads [0,2] a
[0,4,8]

lasts :: Dims -> Array a -> Array a Source #

Select the last element along the supplied dimensions.

>>> pretty $ lasts [0,2] a
[15,19,23]

tails :: Dims -> Array a -> Array a Source #

Select the tail elements along the supplied dimensions.

>>> pretty $ tails [0,2] a
[[[13,14,15],
  [17,18,19],
  [21,22,23]]]

inits :: Dims -> Array a -> Array a Source #

Select the init elements along the supplied dimensions.

>>> pretty $ inits [0,2] a
[[[0,1,2],
  [4,5,6],
  [8,9,10]]]

Function application

extracts :: Dims -> Array a -> Array (Array a) Source #

Extracts dimensions to an outer layer.

>>> pretty $ shape <$> extracts [0] a
[[3,4],[3,4]]

reduces :: Dims -> (Array a -> b) -> Array a -> Array b Source #

Reduce along specified dimensions, using the supplied fold.

>>> pretty $ reduces [0] sum a
[66,210]
>>> pretty $ reduces [0,2] sum a
[[12,15,18,21],
 [48,51,54,57]]

joins :: Dims -> Array (Array a) -> Array a Source #

Join inner and outer dimension layers by supplied dimensions. No checks on shape.

>>> let e = extracts [1,0] a
>>> let j = joins [1,0] e
>>> a == j
True

joinsSafe :: Dims -> Array (Array a) -> Maybe (Array a) Source #

Join inner and outer dimension layers by supplied dimensions. Check inner layer shape.

>>> let e = extracts [1,0] a
>>> (Just j) = joinsSafe [1,0] e
>>> a == j
True

join :: Array (Array a) -> Array a Source #

Join inner and outer dimension layers in outer dimension order.

>>> a == join (extracts [0,1] a)
True

joinSafe :: Array (Array a) -> Maybe (Array a) Source #

Join inner and outer dimension layers in outer dimension order, checking for consistent inner dimension shape.

>>> joinSafe (extracts [0,1] a)
Just (UnsafeArray [2,3,4] [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23])

traverses :: Applicative f => Dims -> (a -> f b) -> Array a -> f (Array b) Source #

Traverse along specified dimensions.

traverses [1] print (range [2,3]) 0 3 1 4 2 5 UnsafeArray [2,3] [(),(),(),(),(),()]

maps :: Dims -> (Array a -> Array b) -> Array a -> Array b Source #

Maps a function along specified dimensions.

>>> pretty $ maps [1] transpose a
[[[0,12],
  [4,16],
  [8,20]],
 [[1,13],
  [5,17],
  [9,21]],
 [[2,14],
  [6,18],
  [10,22]],
 [[3,15],
  [7,19],
  [11,23]]]

filters :: Dims -> (Array a -> Bool) -> Array a -> Array a Source #

Filters along specified dimensions (which are flattened).

>>> pretty $ filters [0,1] (any ((==0) . (`mod` 7))) a
[[0,1,2,3],
 [4,5,6,7],
 [12,13,14,15],
 [20,21,22,23]]

zips :: Dims -> (Array a -> Array b -> Array c) -> Array a -> Array b -> Array c Source #

Zips two arrays with a function along specified dimensions.

>>> pretty $ zips [0,1] (zipWith (,)) a (reverses [0] a)
[[[(0,12),(1,13),(2,14),(3,15)],
  [(4,16),(5,17),(6,18),(7,19)],
  [(8,20),(9,21),(10,22),(11,23)]],
 [[(12,0),(13,1),(14,2),(15,3)],
  [(16,4),(17,5),(18,6),(19,7)],
  [(20,8),(21,9),(22,10),(23,11)]]]

zipsSafe :: Dims -> (Array a -> Array b -> Array c) -> Array a -> Array b -> Maybe (Array c) Source #

Zips two arrays with a function along specified dimensions, checking shapes.

>>> zipsSafe [0] (zipWith (,)) (asArray [1::Int]) (asArray [1,2::Int])
Nothing

modifies :: (Array a -> Array a) -> Dims -> [Int] -> Array a -> Array a Source #

Modify using the supplied function along dimensions & positions.

>>> pretty $ modifies (fmap (100+)) [2] [0] a
[[[100,1,2,3],
  [104,5,6,7],
  [108,9,10,11]],
 [[112,13,14,15],
  [116,17,18,19],
  [120,21,22,23]]]

diffs :: Dims -> [Int] -> (Array a -> Array a -> Array b) -> Array a -> Array b Source #

Apply a binary function between successive slices, across dimensions and lags.

>>> pretty $ diffs [1] [1] (zipWith (-)) a
[[[4,4,4,4],
  [4,4,4,4]],
 [[4,4,4,4],
  [4,4,4,4]]]

Array expansion & contraction

expand :: (a -> b -> c) -> Array a -> Array b -> Array c Source #

Product two arrays using the supplied binary function.

For context, if the function is multiply, and the arrays are tensors, then this can be interpreted as a tensor product. The concept of a tensor product is a dense crossroad, and a complete treatment is elsewhere. To quote the wiki article:

... the tensor product can be extended to other categories of mathematical objects in addition to vector spaces, such as to matrices, tensors, algebras, topological vector spaces, and modules. In each such case the tensor product is characterized by a similar universal property: it is the freest bilinear operation. The general concept of a "tensor product" is captured by monoidal categories; that is, the class of all things that have a tensor product is a monoidal category.

>>> x = array [3] [1,2,3]
>>> pretty $ expand (*) x x
[[1,2,3],
 [2,4,6],
 [3,6,9]]

Alternatively, expand can be understood as representing the permutation of element pairs of two arrays, so like the Applicative List instance.

>>> i2 = indices [2,2]
>>> pretty $ expand (,) i2 i2
[[[[([0,0],[0,0]),([0,0],[0,1])],
   [([0,0],[1,0]),([0,0],[1,1])]],
  [[([0,1],[0,0]),([0,1],[0,1])],
   [([0,1],[1,0]),([0,1],[1,1])]]],
 [[[([1,0],[0,0]),([1,0],[0,1])],
   [([1,0],[1,0]),([1,0],[1,1])]],
  [[([1,1],[0,0]),([1,1],[0,1])],
   [([1,1],[1,0]),([1,1],[1,1])]]]]

coexpand :: (a -> b -> c) -> Array a -> Array b -> Array c Source #

Like expand, but permutes the first array first, rather than the second.

>>> pretty $ expand (,) v (fmap (+3) v)
[[(0,3),(0,4),(0,5)],
 [(1,3),(1,4),(1,5)],
 [(2,3),(2,4),(2,5)]]
>>> pretty $ coexpand (,) v (fmap (+3) v)
[[(0,3),(1,3),(2,3)],
 [(0,4),(1,4),(2,4)],
 [(0,5),(1,5),(2,5)]]

contract :: Dims -> (Array a -> b) -> Array a -> Array b Source #

Contract an array by applying the supplied (folding) function on diagonal elements of the dimensions.

This generalises a tensor contraction by allowing the number of contracting diagonals to be other than 2.

>>> pretty $ contract [1,2] sum (expand (*) m (transpose m))
[[5,14],
 [14,50]]

prod :: Dims -> Dims -> (Array c -> d) -> (a -> b -> c) -> Array a -> Array b -> Array d Source #

Product two arrays using the supplied function and then contract the result using the supplied matching dimensions and function.

>>> pretty $ prod [1] [0] sum (*) (range [2,3]) (range [3,2])
[[10,13],
 [28,40]]

With full laziness, this computation would be equivalent to:

f . diag <$> extracts ds' (expand g a b)

dot :: (Array c -> d) -> (a -> b -> c) -> Array a -> Array b -> Array d Source #

A generalisation of a dot operation, which is a multiplicative expansion of two arrays and sum contraction along the middle two dimensions.

matrix multiplication

>>> pretty $ dot sum (*) m (transpose m)
[[5,14],
 [14,50]]

inner product

>>> pretty $ dot sum (*) v v
5

matrix-vector multiplication Note that an Array with shape [3] is neither a row vector nor column vector.

>>> pretty $ dot sum (*) v (transpose m)
[5,14]
>>> pretty $ dot sum (*) m v
[5,14]

mult :: Num a => Array a -> Array a -> Array a Source #

Array multiplication.

matrix multiplication

>>> pretty $ mult m (transpose m)
[[5,14],
 [14,50]]

inner product

>>> pretty $ mult v v
5

matrix-vector multiplication

>>> pretty $ mult v (transpose m)
[5,14]
>>> pretty $ mult m v
[5,14]

windows :: [Int] -> Array a -> Array a Source #

windows xs are xs-sized windows of an array

>>> shape $ windows [2,2] (range [4,3,2])
[3,2,2,2,2]

Search

find :: Eq a => Array a -> Array a -> Array Bool Source #

Find the starting positions of occurences of one array in another.

>>> a = cycle [4,4] (range [3]) :: Array Int
>>> i = array [2,2] [1,2,2,0] :: Array Int
>>> pretty $ find i a
[[False,True,False],
 [True,False,False],
 [False,False,True]]

findNoOverlap :: Eq a => Array a -> Array a -> Array Bool Source #

Find the ending positions of one array in another except where the array overlaps with another copy.

>>> a = konst [5,5] 1 :: Array Int
>>> i = konst [2,2] 1 :: Array Int
>>> pretty $ findNoOverlap i a
[[True,False,True,False],
 [False,False,False,False],
 [True,False,True,False],
 [False,False,False,False]]

findIndices :: Eq a => Array a -> Array a -> Array [Int] Source #

Find the indices of the starting location of one array in another.

>>> b = cycle [4,4] (range [3]) :: Array Int
>>> i = array [2,2] [1,2,2,0] :: Array Int
>>> pretty $ findIndices i b
[[0,1],[1,0],[2,2]]

isPrefixOf :: Eq a => Array a -> Array a -> Bool Source #

Check if the first array is a prefix of the second

>>> isPrefixOf (array [2,2] [0,1,4,5]) a
True

isSuffixOf :: Eq a => Array a -> Array a -> Bool Source #

Check if the first array is a suffix of the second

>>> isSuffixOf (array [2,2] [18,19,22,23]) a
True

isInfixOf :: Eq a => Array a -> Array a -> Bool Source #

Check if the first array is an infix of the second

>>> isInfixOf (array [2,2] [18,19,22,23]) a
True

Shape manipulations

fill :: a -> Array a -> Array a Source #

Fill an array with the supplied value without regard to the original shape or cut the array values to match array size.

validate (def x a) == True
>>> pretty $ fill 0 (array [3] [])
[0,0,0]
>>> pretty $ fill 0 (array [3] [1..4])
[1,2,3]

cut :: [Int] -> Array a -> Array a Source #

Cut an array to form a new (smaller) shape. Errors if the new shape is larger. The old array is reranked to the rank of the new shape first.

>>> cut [2] (array [4] [0..3] :: Array Int)
UnsafeArray [2] [0,1]

cutSuffix :: [Int] -> Array a -> Array a Source #

Cut an array to form a new (smaller) shape, using suffix elements. Errors if the new shape is larger. The old array is reranked to the rank of the new shape first.

>>> cutSuffix [2,2] a
UnsafeArray [2,2] [18,19,22,23]

pad :: a -> [Int] -> Array a -> Array a Source #

Pad an array to form a new shape, supplying a default value for elements outside the shape of the old array. The old array is reranked to the rank of the new shape first.

>>> pad 0 [5] (array [4] [0..3] :: Array Int)
UnsafeArray [5] [0,1,2,3,0]

lpad :: a -> [Int] -> Array a -> Array a Source #

Left pad an array to form a new shape, supplying a default value for elements outside the shape of the old array.

>>> lpad 0 [5] (array [4] [0..3] :: Array Int)
UnsafeArray [5] [0,0,1,2,3]
>>> pretty $ lpad 0 [3,3] (range [2,2] :: Array Int)
[[0,0,0],
 [0,0,1],
 [0,2,3]]

reshape :: [Int] -> Array a -> Array a Source #

Reshape an array (with the same or less number of elements).

>>> pretty $ reshape [4,3,2] a
[[[0,1],
  [2,3],
  [4,5]],
 [[6,7],
  [8,9],
  [10,11]],
 [[12,13],
  [14,15],
  [16,17]],
 [[18,19],
  [20,21],
  [22,23]]]

flat :: Array a -> Array a Source #

Make an Array single dimensional.

>>> pretty $ flat (range [2,2])
[0,1,2,3]
>>> pretty (flat $ toScalar 0)
[0]

repeat :: [Int] -> Array a -> Array a Source #

Reshape an array, repeating the original array. The shape of the array should be a suffix of the new shape.

>>> pretty $ repeat [2,2,2] (array [2] [1,2])
[[[1,2],
  [1,2]],
 [[1,2],
  [1,2]]]
repeat ds (toScalar x) == konst ds x

cycle :: [Int] -> Array a -> Array a Source #

Reshape an array, cycling through the elements without regard to the original shape.

>>> pretty $ cycle [2,2,2] (array [3] [1,2,3])
[[[1,2],
  [3,1]],
 [[2,3],
  [1,2]]]

rerank :: Int -> Array a -> Array a Source #

Change rank by adding new dimensions at the front, if the new rank is greater, or combining dimensions (from left to right) into rows, if the new rank is lower.

>>> shape (rerank 4 a)
[1,2,3,4]
>>> shape (rerank 2 a)
[6,4]
flat == rerank 1

reorder :: Dims -> Array a -> Array a Source #

Change the order of dimensions.

>>> pretty $ reorder [2,0,1] a
[[[0,4,8],
  [12,16,20]],
 [[1,5,9],
  [13,17,21]],
 [[2,6,10],
  [14,18,22]],
 [[3,7,11],
  [15,19,23]]]

squeeze :: Array a -> Array a Source #

Remove single dimensions.

>>> let sq = array [2,1,3,4,1] [1..24] :: Array Int
>>> shape $ squeeze sq
[2,3,4]
>>> shape $ squeeze (singleton 0)
[]

elongate :: Dim -> Array a -> Array a Source #

Insert a single dimension at the supplied position.

>>> shape $ elongate 1 a
[2,1,3,4]
>>> elongate 0 (toScalar 1)
UnsafeArray [1] [1]

transpose :: Array a -> Array a Source #

Reverse indices eg transposes the element Aijk to Akji.

>>> index (transpose a) [1,0,0] == index a [0,0,1]
True
>>> pretty $ transpose (array [2,2,2] [1..8])
[[[1,5],
  [3,7]],
 [[2,6],
  [4,8]]]

inflate :: Dim -> Int -> Array a -> Array a Source #

Inflate an array by inserting a new dimension given a supplied dimension and size.

alt name: replicate

>>> pretty $ inflate 0 2 (array [3] [0,1,2])
[[0,1,2],
 [0,1,2]]

intercalate :: Dim -> Array a -> Array a -> Array a Source #

Intercalate an array along dimensions.

>>> pretty $ intercalate 2 (konst [2,3] 0) a
[[[0,0,1,0,2,0,3],
  [4,0,5,0,6,0,7],
  [8,0,9,0,10,0,11]],
 [[12,0,13,0,14,0,15],
  [16,0,17,0,18,0,19],
  [20,0,21,0,22,0,23]]]

intersperse :: Dim -> a -> Array a -> Array a Source #

Intersperse an element along dimensions.

>>> pretty $ intersperse 2 0 a
[[[0,0,1,0,2,0,3],
  [4,0,5,0,6,0,7],
  [8,0,9,0,10,0,11]],
 [[12,0,13,0,14,0,15],
  [16,0,17,0,18,0,19],
  [20,0,21,0,22,0,23]]]

concats :: Dims -> Int -> Array a -> Array a Source #

Concatenate and replace dimensions, creating a new dimension at the supplied postion.

>>> pretty $ concats [0,1] 1 a
[[0,4,8,12,16,20],
 [1,5,9,13,17,21],
 [2,6,10,14,18,22],
 [3,7,11,15,19,23]]

reverses :: Dims -> Array a -> Array a Source #

Reverses element order along specified dimensions.

>>> pretty $ reverses [0,1] a
[[[20,21,22,23],
  [16,17,18,19],
  [12,13,14,15]],
 [[8,9,10,11],
  [4,5,6,7],
  [0,1,2,3]]]

rotates :: Dims -> [Int] -> Array a -> Array a Source #

Rotate an array by/along dimensions & offsets.

>>> pretty $ rotates [1] [2] a
[[[8,9,10,11],
  [0,1,2,3],
  [4,5,6,7]],
 [[20,21,22,23],
  [12,13,14,15],
  [16,17,18,19]]]

Sorting

sorts :: Ord a => Dims -> Array a -> Array a Source #

Sort an array along the supplied dimensions.

>>> sorts [0] (array [2,2] [2,3,1,4])
UnsafeArray [2,2] [1,4,2,3]
>>> sorts [1] (array [2,2] [2,3,1,4])
UnsafeArray [2,2] [2,3,1,4]
>>> sorts [0,1] (array [2,2] [2,3,1,4])
UnsafeArray [2,2] [1,2,3,4]

sortsBy :: Ord b => Dims -> (Array a -> Array b) -> Array a -> Array a Source #

The indices into the array if it were sorted by a comparison function along the dimensions supplied.

>>> import Data.Ord (Down (..))
>>> sortsBy [0] (fmap Down) (array [2,2] [2,3,1,4])
UnsafeArray [2,2] [2,3,1,4]

orders :: Ord a => Dims -> Array a -> Array Int Source #

The indices into the array if it were sorted along the dimensions supplied.

>>> orders [0] (array [2,2] [2,3,1,4])
UnsafeArray [2] [1,0]

ordersBy :: Ord b => Dims -> (Array a -> Array b) -> Array a -> Array Int Source #

The indices into the array if it were sorted by a comparison function along the dimensions supplied.

>>> import Data.Ord (Down (..))
>>> ordersBy [0] (fmap Down) (array [2,2] [2,3,1,4])
UnsafeArray [2] [0,1]

Transmission

transmit :: (Array a -> Array b -> Array c) -> Array a -> Array b -> Array c Source #

Apply a binary array function to two arrays where the shape of the first array is a prefix of the second array. No checks on shape.

>>> a = array [2,3] [0..5]
>>> pretty $ transmit (zipWith (+)) (toScalar 1) a
[[1,2,3],
 [4,5,6]]

transmitSafe :: (Array a -> Array b -> Array c) -> Array a -> Array b -> Maybe (Array c) Source #

Apply a binary array function to two arrays where the shape of the first array is a prefix of the second array. Checks shape.

>>> a = array [2,3] [0..5]
>>> transmitSafe (zipWith (+)) (array [3] [1,2,3]) a
Nothing

transmitOp :: (a -> b -> c) -> Array a -> Array b -> Array c Source #

Transmit an operation if the first array is a prefix of the second or vice versa.

>>> pretty $ transmitOp (*) a (asArray [1,2])
[[[0,1,2,3],
  [4,5,6,7],
  [8,9,10,11]],
 [[24,26,28,30],
  [32,34,36,38],
  [40,42,44,46]]]

telecasts :: Dims -> Dims -> (Array a -> Array b -> Array c) -> Array a -> Array b -> Array c Source #

Apply a binary array function to two arrays with matching shapes across the supplied dimensions. No check on shapes.

>>> a = array [2,3] [0..5]
>>> b = array [3] [0..2]
>>> pretty $ telecasts [1] [0] (concatenate 0) a b
[[0,1,2],
 [3,4,5],
 [0,1,2]]

telecastsSafe :: Dims -> Dims -> (Array a -> Array b -> Array c) -> Array a -> Array b -> Maybe (Array c) Source #

Apply a binary array function to two arrays with matching shapes across the supplied dimensions. Checks shape.

>>> a = array [2,3] [0..5]
>>> b = array [1] [1]
>>> telecastsSafe [0] [0] (zipWith (+)) a b
Nothing

Row specializations

pattern (:<) :: Array a -> Array a -> Array a infix 5 Source #

Convenience pattern for row extraction and consolidation at the beginning of an Array.

>>> (x:<xs) = array [4] [0..3]
>>> x
UnsafeArray [] [0]
>>> xs
UnsafeArray [3] [1,2,3]
>>> (x:<xs)
UnsafeArray [4] [0,1,2,3]

cons :: Array a -> Array a -> Array a Source #

Add a new row

>>> pretty $ cons (array [2] [0,1]) (array [2,2] [2,3,4,5])
[[0,1],
 [2,3],
 [4,5]]

uncons :: Array a -> (Array a, Array a) Source #

split an array into the first row and the remaining rows.

>>> uncons (array [3,2] [0..5])
(UnsafeArray [2] [0,1],UnsafeArray [2,2] [2,3,4,5])

pattern (:>) :: Array a -> Array a -> Array a infix 5 Source #

Convenience pattern for row extraction and consolidation at the end of an Array.

>>> (xs:>x) = array [4] [0..3]
>>> x
UnsafeArray [] [3]
>>> xs
UnsafeArray [3] [0,1,2]
>>> (xs:>x)
UnsafeArray [4] [0,1,2,3]

snoc :: Array a -> Array a -> Array a Source #

Add a new row at the end

>>> pretty $ snoc (array [2,2] [0,1,2,3]) (array [2] [4,5])
[[0,1],
 [2,3],
 [4,5]]

unsnoc :: Array a -> (Array a, Array a) Source #

split an array into the initial rows and the last row.

>>> unsnoc (array [3,2] [0..5])
(UnsafeArray [2,2] [0,1,2,3],UnsafeArray [2] [4,5])

Shape specializations

iota :: Int -> Array Int Source #

Vector specialisation of range

>>> iota 5
UnsafeArray [5] [0,1,2,3,4]

Math

uniform :: (StatefulGen g m, UniformRange a) => g -> [Int] -> (a, a) -> m (Array a) Source #

Generate an array of uniform random variates between a range.

>>> import System.Random.Stateful hiding (uniform)
>>> g <- newIOGenM (mkStdGen 42)
>>> u <- uniform g [2,3,4] (0,9 :: Int)
>>> pretty u
[[[0,7,0,2],
  [1,7,4,2],
  [5,9,8,2]],
 [[9,8,1,0],
  [2,2,8,2],
  [2,8,0,6]]]

invtri :: Fractional a => Array a -> Array a Source #

Inversion of a Triangular Matrix

>>> t = array [3,3] ([1,0,1,0,1,2,0,0,1] :: [Double]) :: Array Double
>>> pretty (invtri t)
[[1.0,0.0,-1.0],
 [0.0,1.0,-2.0],
 [0.0,0.0,1.0]]
>>> ident (shape t) == mult t (invtri t)
True

inverse :: Floating a => Array a -> Array a Source #

Inverse of a square matrix.

>>> e = array [3,3] [4,12,-16,12,37,-43,-16,-43,98] :: Array Double
>>> pretty (inverse e)
[[49.36111111111111,-13.555555555555554,2.1111111111111107],
 [-13.555555555555554,3.7777777777777772,-0.5555555555555555],
 [2.1111111111111107,-0.5555555555555555,0.1111111111111111]]
mult (inverse a) a == a

chol :: Floating a => Array a -> Array a Source #

Cholesky decomposition using the Cholesky-Crout algorithm.

>>> e = array [3,3] [4,12,-16,12,37,-43,-16,-43,98] :: Array Double
>>> pretty (chol e)
[[2.0,0.0,0.0],
 [6.0,1.0,0.0],
 [-8.0,5.0,3.0]]
>>> mult (chol e) (transpose (chol e)) == e
True