Many-dimensional type-safe numeric ops
The project consists of two parts:
dimensions
is a library to support type-level operations on lists of dimensions;easytensor
wraps low-level operations on primitive byte arrays in a type-safe data type indexed over an element type and a list of dimensions.Data.Type.List
and Data.Type.Lits
provide type-level operations on lists, Nat
s, and Symbols
s.
Numeric.TypedList
is the core module providing a typelist-indexed type that is, in fact, just a newtype wrapper on a plain haskell list.
The TypedList (f :: k -> Type) (xs :: [k])
represents a lot of things; by changing type parameter f
, I use TypedList
as a flexible (yet typesafe) tuple, a finite dimensions list, or an index over such a list.
Numeric.Dimensions
provides:
XNat = N Nat | XN
similar to type Maybe
; kind XNat
is used for type variables when some of dimensions in a type-level list are not known at compile time;Dims (ds :: [k])
, where k
is either Nat
or XNat
, together with class Dimensions ds
it allows lots of type-level operations on type-level dimensionality;Idxs (ds :: [k])
is used to index over many-dimensional space defined by Dims ds
.This library aims at providing fast, simple, and useful geometry types for use in computer graphics and low-dimensional physics modelling.
All geometry types implement standard Prelude
numerical classes, such as Num
, Fractional
, Floating
, lexicographical Ord
favouring element-wise operations:
functions like +
, *
, /
are all element-wise.
Common matrix and vector operations are provided separately.
Data type DataFrame
is presented in two versions:
DataFrame t (ds :: [Nat])
- dimensionality of a dataframe is totally known at compile time.DataFrame t (ds :: [XNat]
- some dimensions may be known at runtime only.Parameter t
of a DataFrame can be arbitrary type that has an instance of PrimBytes
.
PrimBytes
typeclass can be automatically derived using Generics
.
This mechanics allows creating interleaved arrays, e.g. DataFrame (Double, Float, Float) ds
.
Parameter t
of a DataFrame
can also be a list of PrimBytes
types:
this way, DataFrame
consists of several "columns" or "variables" of different types and same dimensionality.
Behind the scenes all data types are implemented as primitive values or primitive byte arrays, aiming at maximum performance.
Tricky layers of newtypes and closed type families (which are not exposed to a user) allow some kind of ad-hoc polymorphism:
for example, Vector t n
implemented as ByteArray#
is overloaded by a specialized FloatX2 Float# Float#
in case of Vector Float 2
.
The packages are tested on GHC 8.4+.
dimensions
may work on GHC 8.2, but the corresponding tests were dropped.
Support of easytensor
on GHC 8.2 was dropped due to:
Semigroup
-Monoid
storyEverything! In general, v2 of easytensor
is meant to show an expectable behavior:
Show
and Read
instances look like automatically-generated instances for algebraic data types.Enum
more intuitive.Dims
list is reversed (the first dimension is "the most significant"), thus making Ord
instances of Dims
and Idxs
coincide with conventional Haskell lexicographic ordering. The implication of this is the next two points.DataFrame
Ord
instances are now proper total lexicographic ordering.DataFrame
layout now is row-first instead of column-first. Therefore, to keep low-level SIMD optimizations of 3D geometry possible, I've had to transpose all matrices in HomTransform4
class.Nat
-indexed Dims
, Idxs
, and DataFrame
now have Generic
and Data
instances that make these datatypes look like ordinary algebraic data types.DataFrame
s from pieces.