Hello,
I always wanted to have a multidimentional array library (something like numpy) for prolog.
So I started working on a proof of concept here: GitHub - kwon-young/array: swi-prolog array library
Here is a list of features from the top of my head:
- Nd Array creation:
- with an empty buffer
- with ones or arange (notably these won’t be allocated)
- from a prolog list
- reshaping
- indexing and fancy indexing
- transpose
- broadcasting
- basic math operation: +, -, *, /, sum and matrix multiplication with the operator
@
- all array operation is done using the affectation operator
@=
, similar to the#=
operator in clpfd - actual computation is done in C, so should be faster than prolog, but is still much slower than specialized compute library like BLAS
Here is an example code of how you would do a matmul with this library:
?- Size = 1024,
A @= array{name:a, dtype: float64}.empty([Size, Size], [zero(true)]),
B @= array{name:b, dtype: float64}.empty([Size, Size], [zero(true)]),
C @= A @ B,
realize([C]).
or a more complex chain of operation:
?- C @= array{}.arange(12).reshape([3, 2, 2]).sum([0, 2], false),
R = C.to_list().
C = ...
R = [27, 39].
My main motivation here is to write everything from scratch and try to leverage prolog specific strength as much as possible along the way. So, here are some of my design decisions:
- This library implements the strided array abstraction (like numpy), meaning that multidimentional arrays are backed by a flat buffer and iterated through using the shape and stride information.
- interestingly, all shape and strides computation is done through clpfd, making this a mostly logically pure implementation of the concept of strides. This means that you can do test/generate/search over strides and shapes of a computational graph.
- The design is mostly inspired from tinygrad where the operations are lazily defined to form a graph. This graph is then compiled in C and executed through the predicate
realize/1
.- Using prolog was a really pleasure when working on the generation of the computational graph. I can just nest dicts inside dicts and it works really well for now.
- I use swi prolog dicts for representing an array and it works really well for this. It even allows me to have a functional api to define operation on arrays like
sum
. - The implementation features a state-of-the-art static allocation algorithm called minimalloc. I’ve spent weeks trying to implement it perfectly, hence the effort for the documentation.
- In order to easily generate C code, I have written a god forsaken hacked up C code generator, which I’m both proud and ashamed of…
Unfortunately, this PoC is quite unreadable, full of bugs and extremly slow and completely undocumented. And I’m starting to loose a bit of steam for this.
So if anyone is interested or have question, let me know so that I keep working on this.
In the future, I want to add autograd for doing deep learning and an opencl backend for heterogeneous computing (gpu computing in prolog!). If anyone wants to help reach these goals, let me know, here or in a issue in the github repo.