Dimensions ix :: Natis exactly that, but I get a feeling that you want the former. I contemplated on adding array size to the type level, but that would complicate the library significantly. So I decided if I ever get time I will create a wrapper library that has the ability to reason about array sizes at compile time, but for now it is pretty far away on the list of things I'd like to do. Here is one library that augments a massiv matrix with type level size:
mapalgebra (eg. [Raster`](https://hackage.haskell.org/package/mapalgebra-0.2.1/docs/Geography-MapAlgebra.html#t:Raster))
inner :: (Backprop a, Num a, Reifies s W) => BVar s (Vector D a) -> BVar s (Vector D a) -> BVar s a inner = liftOp2 . op2 $ \ u v -> ( sum $ unsafeLiftArray2 (*) u v , \dzdy -> (dzdy *. v, u .* dzdy) ) -- | this instance only exists for Delayed arrays instance (Index ix, Backprop e) => Backprop (Array D ix e) where zero = fmap zero one = fmap one add = zipWith add
D. The only thing I can recommend is creating a newtype wrapper with phantom type variable around
Arraythat would carry the size at the type level and create a
Backpropinstance for that type instead. It is currently not possible to add this functionality to massiv without major breaking and I don't think I would want to do that either. Not all people care about types that much. But I do see a future where we have a library that implements this functionality on top of massiv.
Hi @pchiusano This is pretty awesome that you are considering massiv for Unison language, hope it works out for you.
Current handling of Bool type depends on the representation, but the only ones that do unboxing are
S. The former is the same as unboxing of Bool from
Data.Vector.Unboxed, the latter is the
Storable instance. Neither one of them will do any tight packing, for
Unbox it will use a
Word8, while for
Storable it is even worse and each element will take 4 bytes since it is stored as
Int32. This is a bit unfortunate, but it will still be faster than if
Bool is treated as boxed.
That being said, there is a way to get support for tightly packed Bools with massiv by using https://github.com/Bodigrim/bitvec The
Bit type has
Unbox instance and it will work with
With regards to scans I simply haven't gotten to them yet, but it would be fairly easy to add them, since all the stream stuff (
DS representation) in massiv is a wrapper around the vector streams.
foo * x + ythen I'm guessing that can get optimized really well with sufficient inlining... but now suppose that expression is being created dynamically, like in the front end of a programming language. Now static inlining doesn't do anything. Is it going to end up with everything being boxed and/or with intermediate vectors being created? @lehins
Devil is in the details. First of all what do you mean by "computations are being created dynamically"? Second of all what are the types of
Intermediate vectors in massiv are not being created on its own. Unlike vector package or lists in Haskell, massiv is structured in such a way that the user is in control of fusion of computation, in other words functions like
compute do allocations and computation, while
* create delayed computation. With respect to inlining it is a crucial optimization and if the code fails to inline because a program was not compiled with optimizations then performance will be terrible, no matter which numeric library you use in Haskell.
Boxed and Unboxed is also known statically thanks to representations
map (1+) . map (2*) . fromList Seq [1,2,3]
computeAs Uof that
computeAs U . map (1+) . map (2*) . fromList Seq [1,2,3]as Haskell code, I can expect a lot of it to get inlined and performance to be good, right?
fromListwill require you to provide a manifest type too. So
fromList Seq [1,2,3] :: Vector U Intwill allocate an unboxed vector with contents
[1,2,3], then both maps will create delayed arrays (so no allocations), then
computeAs Uwill allocate another vector and load elements
[3,5,7]into the contents of that vector. And yes, performance of that will be great.
computeAs U . map (1+) . map (2*) . fromList Seq [1,2,3] :: Vector U Int, what would it end up doing?
it's still not going to materialize the intermediate arrays, right
That is correct. materialization is controlled manually
what about the map (1+) ... could that end up calling a boxed Int -> Int function
in case of
map (1+) in massiv it is essentially just function composition. But, considering that
Int in ghc is a boxed value around
Int#, compiling without optimizations probably will cause all the intermediate boxing/unboxing, but that is the cost of interpreting Haskell vs compiling it, regardless of a library you use.
smap. Latter can be only processed sequentially and can only deal with flat vectors because it operates on streams, while the former can work on multidimensional arrays and can be parallelized. This is pretty much the difference between the
Darrays, most often
Vector D Double. Most of them are based on folds or
scanlM(https://hackage.haskell.org/package/conduit-22.214.171.124/docs/Data-Conduit-Combinators.html#v:scanlM). Either way, running these operations gets progressively slower, which I think is an indication of a space leak. I tried both strict and lazy consumers, like closing the conduit with a
printCor sinking the results into a list and printing that, with similar behaviours
something that's been bugging me for a while: why can only D arrays be Numeric?
In short, numeric interface is still going through design and
Numeric class was created to add more efficient numeric implementation, hopefully with some SIMD support, but that is still quite far away in the future, so for now it is only
D that works with
Why is only
D, because if you write something like
compute (x + y * z) you want the numeric operations to fuse. If representations like
U would have an instance then every numeric operation would cause an allocation of a completely new array, which would quickly become very slow.
There is no point to use
D array with conduit. Delayed array do not take up any memory, so constant memory processing approach doesn't make sense. On the other hand, if you producer is actually allocating new arrays, then you really want to call
compute for each scan operation in your pipeline, however this will cause allocation of a whole new array as well for each iteration. So you might want to look into
computeInto, in order to reuse the same region of memory at each step. Of course, devil is in the details.
@ocramz Anyways, as I I've mentioned before, I am not that big on discussing code abstractly, so if you have some snippets I can point you in the right direction. Also
seq will be of no use with delayed arrays, since
D doesn't rely on Haskell laziness. Think of
compute as being a
seq for massiv.