numericals
2023-02-15
A high performance numerical computing library for Common Lisp (focus: basic math operations)
Upstream URL
Author
License
numericals
THIS PROJECT IS UNSTABLE YET. THINGS ARE SUBJECT TO CHANGE.
See digikar99.github.io/numericals/ for documentation.
The project intends to offer a numpy-like (not exactly numpy!) API and equivalent performance for high performance number crunching. This is enabled by the use of SIMD using libraries like BMAS backed by SLEEF as well as BLAS. This is further coupled with multithreading using lparallel.
It provides mainly two ASDF systems: numericals
for use with cl:array
and dense-numericals
for use with dense-arrays:array, that is to say, wherever possible functionality in numericals
attempts to output cl:array
, while wherever possible, dense-numericals
attempts to output dense-arrays:array
.
At the moment, work is primarily happening under dense-numericals
, so it might be slightly better than numericals
; raise an issue if you'd like to shift the focus to numericals
.
Usage and Features
Native CL arrays
As of this writing,
- The only libraries that offer broadcasted operations on arrays are this and numcl
numcl
does not yet have a focus on high performance - though, it should be possible to implement the current einsum based backend using BLAS and BMAS; instead the focus there is on functionality; by contrast, the focus here is on performance first, and functionality second. Users do not have to choose.:mix
option ofuiop:define-package
can be useful for mixing the two libraries as per user preferences- Other minor differences wrt numcl include:
- Both
(ones 2 3 :type 'single-float)
and(ones '(2 3) :type 'single-float)
are legal in numericals; while only the latter is legal in numcl/numpy numericals
provides a*array-element-type-alist*
equivalent toswank:*readtable-alist*
to provide a package local way of specifying the default element-type for arrays. This can be further overriden by binding*array-element-type*
. This does impose performance penalties however.numcl
relies on JIT backed by specialized-function, whilenumericals
relies on AOT backed by polymorphic-functions and cl-form-types. Again, these are not either-or, high level user functions can (in theory) utilize specialized-function, while the innards can use static-dispatch either by polymorphic-functions or static-dispatch or fast-generic-functions.
- Both
- In addition to these two, another performant library operating on CL arrays includes lla. Again,
uiop:define-package
with:mix
can be used suitably.
The author of numericals
did not find other libraries operating on native CL arrays.
Non-native CL arrays
There are quite a few libraries in Common Lisp in this domain. I have only managed to take a peak at femlisp-matlisp.
That said, the goal of numericals
is not to replace python ecosystems, at least not in the short run, but instead to overcome the limitations of libraries like py4cl/2 of sub-10,000 instructions per second.
Usage
For the time being, preferably, fetch from this dist of ultralisp. Once (ql:quickload "numericals")
or (ql:quickload "dense-numericals")
is successful; use inside your own package using :mix
option of uiop:define-package
(see above discussion), or package-local-nicknames.
Run tests using (asdf:test-system "numericals")
or (asdf:test-system "dense-numericals")
; these are scattered throughout the system.
Why separate numericals
and dense-numericals
packages?
Not doing so would mean an additional configuration option. That implies an usage overhead on the part of the user, besides employing a decision factor into the return type of the functions. Separate packages, functions, symbols simply by-passes this issue. If a user is interfacing with the lisp ecosystem at large, they can choose numericals
without a second thought.
Project Predecessors
The project renaming reflects an attempt to separate the ANSI standard parts of the codebase from the SBCL-specific part, so that a portability attempt may be made in the future.
Background
Curiosity got the better of me one day, and I set out to explore the limits of numerical computing with Common Lisp. I mean - what does speed require? Just memory-locality and SIMD? SBCL has memory-locality. What about SIMD? Well, the functionality hasn't been "standardized" yet, and there are several attempts. Indeed, SBCL needs more documentation - think Emacs! But knowledge exists in people's heads. People are willing to share it. So, this was possible.
PS: This library began as a reddit post, that, in turn, was triggered by this reddit post.
You should probably use the latest SBCL (get from git), at least SBCL-2.0.4. The build is fairly easy: sh make.sh && sh run-sbcl.sh # or install.sh
.
Acknowledgements
- Everyone who has contributed to SBCL.
- u/love5an and u/neil-lindquist for the required hand-holding and the gist.
- Paul Khuong for some blog posts.
- guicho271828 for SBCL Wiki as well as numcl.
- All the SLEEF contributors
- All the contributors of c2ffi and cl-autowrap
- It's possible that I could have forgotten to mention somebody - so... yeah... happy number crunching!
Dependencies (26)
- alexandria
- cffi
- cl-ascii-table
- cl-autowrap
- cl-bmas
- cl-form-types
- compiler-macro-notes
- ctype
- dense-arrays
- extensible-compound-types
- fiveam
- introspect-environment
- iterate
- jsown-utils
- lparallel
- magicl
- numpy-file-format
- policy-cond
- polymorphic-functions
- py4cl2
- slime
- specialized-function
- trivial-coerce
- trivial-package-local-nicknames
- trivial-types
- uiop