Common Lisp is a wonderful language for explorative computing, but often lacks tools for numerical computing. This project is an attempt at plugging this perceived shortcoming.
Overview
Matlisp provides an infix reader for evaluating mathematical expressions. We use '/' as both a binary and unary operator within matlisp's infix reader, this allows us to ensure semantic uniformity even when dealing with noncommutativity rings.
Solving Ax = b is consequently as simple as,
MATLISP> #i(/A * b)
, this operation being generic to A being a scalar/matrix/permutation. It's also extremely simple to define custom syntax by tweaking "src/reader/infix.lisp".
For a variety of reasons, Matlisp duplicates general BLAS functionality using Lisp code. These are similar to the naiveloop versions found in the BLAS reference implementation, but are generated using macros, and are consequently quite terse. These macro expand into lean code blocks which are often very competitive (within 1020%) of the corresponding C code. This allows users greater flexibility when dealing with specific problems. For instance GEMM is essentially generated by the following codesnippet (exercise: why 'j k i' ?),
> (defun mm (A B C)
(einsteinsum #.(tensor 'doublefloat) (j k i) (ref C i j) (* (ref A i j) (ref B j k))))
Matlisp also switches dynamically between BLAS routines (when available) and native lisp code to avoid FFI overheads. This functionality can be used (with minimal tweaking) without the need for a a BLAS library, or for commutativering types which don't have BLAS routines available.
CLOS, Metaobjects
Most classes/methods in Matlisp are dynamically generated for specialized types at runtime. Generating class specialized for certain fieldtypes is not particularly interesting, and takes place along usual lines.
#+BEGIN_{SRC} lisp M> (tensor 'doublefloat 'densetensor)
<BLASMIXIN DENSETENSOR: DOUBLEFLOAT> 
M> (tensor 'doublefloat 'graphtensor)
<GRAPHTENSOR: DOUBLEFLOAT> 
#+END_{SRC}
Tying method generation with CLOS, however, comes with its own set of complications. Consider this notsohypothetical situation: we're given an abstract class densetensor, and we wish to write a method for computing the square of a matrix. This sounds simple enough, and as a first pass, one ends up with something like,
(defgeneric sqmm (x)
(:method :before ((x tensor))
(assert (typep x 'tensorsquarematrix) nil "X must be square")))
A first solution can be as simple as,
(defmethod sqmm ((x densetensor) &aux (clx (classname (classof x))))
(compileandeval
`(defmethod sqmm ((x ,clx) &aux (ret (zeros (dimensions x) (classof x))))
(einsteinsum ,clx (j k i) (ref ret i j) (* (ref x i j) (ref x j k)))))
(sqmm x))
This works because when a subclass of densetensor is encountered which isn't specialized, a method is generated and this method is subsequently called. The next time sqmm is called with an instance of this class, closermop:computeapplicablemethodsusingclasses places this method at the top of the list. This scheme works perfectly if densetensor only has descendents at distance 1 and no more (why ?). Fixing this within the confines of CLOS is troublesome, because every method then needs to check if it is 'special enough'. Cue: Metaobjects to the rescue.
This is solved in Matlisp in three steps, Define a subclass tensormethodgenerator of closermop:funcallablestandardclass (a subclass of a function).
 Define subclasses of closermop:specializer, one each for marking generated function arguments, and generator arguments.
 Define custom method dispatch closermop:computeapplicablemethodsusingclasses for this subclass of genericfunctions.
These definitions can be found in 'src;base;generator.lisp'.
The solution to the question posed is then simply,
(defgeneric sqmm (x)
(:method :before ((x tensor))
(assert (typep x 'tensorsquarematrix) nil "X must be square"))
(:genericfunctionclass tensormethodgenerator))
(defmethod sqmm ((x #.(makeinstance 'groupspecializer :objectclass 'densetensor :groupname :x)) &aux (clx (classname (classof x))))
(compileandeval
`(defmethod sqmm ((x ,(makeinstance 'classpspecializer :objectclass (findclass clx))) &aux (ret (zeros (dimensions x) ',clx)))
(einsteinsum ,clx (j k i) (ref ret i j) (* (ref x i j) (ref x j k)))))
(sqmm x))
Methods with classpspecializer are only selected by closermop:computeapplicablemethodsusingclasses if the argument class match that of the classpspecializer instances which are passed as specializers of the method. Similarly, the groupspecializer is only selected if all arguments are subclasses of objectclass, and all argument classes for a given groupname are the same. The CLOS discriminator also ensures that the latter method has very low precedence. This solves the problem posed earlier and codegenerating 'Templates' essentially end up becoming firstclass functions.
For debuggability and other reasons, this mechanism is abstracted away inside the macro matlisp:definetensormethod.
Dependencies
CFFI
Matlisp uses CFFI for interfacing with foreign libraries. CFFI support varies between lisp implementations; Matlisp assumes that certain simplevectors in the lisp implementation are in unboxed form, and have a SAP that can be passed to Fortran functions. It might be possible to replace this with static/pinned arrays, as is kindof done a the moment for foreign densetensor.
Most of the BLAS functionality is duplicated in native lisp code, and so one can very possibly run Matlisp without any foreign libraries (but at the cost of losing all LAPACK functionality).
CFFI is provided by quicklisp.
closerMOP
Matlisp now uses the MetaObject protocol to generate and maintain classes & methods. This is not part of the ANSI standard, and consequently only seems to work on SBCL at the moment. Your luck with other implementations may wary.
Trivia
You may also want to use the latest version of Trivia pattern matching library. Matlisp uses the ?list matcher which is a recent addition to the project.
Install
Matlisp has been successfully built on SBCL. Getting it running is thematic of quicklisp, Install quicklisp http://www.quicklisp.org/beta/.
 Download matlisp source:
> git clone git@github.com:matlisp/matlisp.git
> ln s $PWD/matlisp <quicklispdirectory>/localprojects
Fire up your lisp implementation and load as usual with quicklisp:
CLUSER> (ql:quickload :matlisp)
CLUSER> (inpackage :matlisp)
M> (namedreadtables:inreadtable :infixdispatchtable)
Matlisp searches for "libblas" (and possibly "liblapack") first in the directory where the ASDF definition file resides, and then in /usr/lib/
(on linux) and then in paths defined by ld.conf
. Linking to something like MKL is as simple as placing a symbolic link by name "libblas.so" in the toplevel directory. See src/package.lisp
for details. Matlisp relies only on the Fortran API (not CBLAS).
Examples
;;Creation
M> (copy! (randomnormal '(2 2)) (zeros '(2 2) '((complex doublefloat))))
#<<BLASMIXIN SIMPLEDENSETENSOR: (COMPLEX DOUBLEFLOAT)> #(2 2)
0.8492 1.976
2.207 1.251
>
;;gemv
M> (let ((a (randomnormal '(2 2)))
(b (randomnormal 2)))
#i(a * b))
#<<BLASMIXIN SIMPLEDENSETENSOR: DOUBLEFLOAT> #(2)
1.1885 0.95746
>
;;Tensor contraction
M> (let ((H (randomnormal '(2 2 2)))
(b (randomnormal 2))
(c (randomnormal 2))
(f (zeros 2)))
;;#i(H @ b @ c)
(einsteinsum #.(tensor 'doublefloat) (i j k) (ref f i) (* (ref H i j k) (ref b j) (ref c k))))
#<<BLASMIXIN SIMPLEDENSETENSOR: DOUBLEFLOAT> #(2)
0.62586 1.1128
>
;;Similarly
M> (let ((H (randomnormal '(2 2 2)))
(x (randomnormal 2))
(y (randomnormal 2)))
#i(H @ x @ y))
#<<BLASMIXIN SIMPLEDENSETENSOR: DOUBLEFLOAT> #(2)
0.3234 0.6201
>
Documentation
More documentation will be added as the project reaches a stable state.
Enchancements

The API for BLAS functions dot ensures inconsistent ABIs between compilers. This package provides a Fortran wrapper (and Lisp methods for `dot`) that fixes these issues. It also provides F77 methods for elementwise division, which follow the `scal` API.e
Weyl
Weyl is a CAS written in Lisp (and for Lisp!) at Cornell by Richard Zippel's group. Currently, this used only within 'src;base;symbolic.lisp' (and assoc. infix readers), for working with symbolic expressions. In order to use this functionality, Weyl must be loaded before Matlisp.
Weyl can installed from 'git@github.com:matlisp/weyl.git'.
Tracker
Completed
 Dynamic class/method generation, using MOP
 Complete BLAS/LAPACK functionality for types doublefloat, singlefloat, (complex singlefloat), (complex doublefloat).
 Partial support for densetensor with a foreignpointer store.
 Inplace slicing, realimaginary part views, negative strides for densetensors.
 permutations, sorting, conversion between action/cycle/flip representations.
 Optimized loop generators (einstein/iterate formod) in Lisp; BLAS functionality duplicated, and switches automatically b/w Lisp and Fortran.
 Arbitrary tensor contraction.
 Graphs: a general CCS/CCR matrix implementation, lisp adjacency list support, iterate macros for DFS/BFS/SFD graphtraversal, treedecomposition, choleskycovers, maximum acyclic subgraph, Djikstra's algorithms.
 Data structures: Fibonacci heap, UnionFind structure, minimal Doubly linked lists.
 Hashtable sparse tensor: O(1) read/write.
 Coordinate sparse tensor
TODO Incomplete/Planned
 Unify slicing syntax Unify the slicing syntax used by iterate formod/einstein macros. Unify these with a more powerful language.
 Automatic Differentiation
 Symbolic Integration Needs extensive hacking of Weyl.
 Gnuplot interface
 (C)Pythonbridge
 FiveAM tests
Emacs
Matlisp uses a variety of Unicode symbols for some function names and certain operators in the infix reader. The user can readily change these to his suiting, or instead use the following Emacs shortcuts to enter these characters.
;; Lisp
(defun addlispslimehook (func)
(addhook 'lispmodehook func)
(addhook 'slimereplmodehook func))
;;#\GREEK_SMALL_LETTER_LAMDA is bound to lambda in :infixdispatchtable; inherited from ?reader
(addlispslimehook #'(lambda () (localsetkey (kbd "Cc \\") (lambda () (interactive (insert "?"))))))
;;#\LONG_RIGHTWARDS_ARROW_FROM_BAR used for anonymous function definition in Infix
;;#i([x] ? x + 1)
(addlispslimehook #'(lambda () (localsetkey (kbd "Cc /") (lambda () (interactive (insert "?"))))))
;;#\CIRCLE_TIMES used for tensorproduct in Infix
(addlispslimehook #'(lambda () (localsetkey (kbd "Cc *") (lambda () (interactive (insert "?"))))))
;;#\MIDDLE_DOT used for tensorcontraction (also bound is @) in Infix
(addlispslimehook #'(lambda () (localsetkey (kbd "Cc .") (lambda () (interactive (insert "?"))))))
;;Used in the function `(?i g &optional i j)` in graphaccessor.lisp
(addlispslimehook #'(lambda () (localsetkey (kbd "Cc a") (lambda () (interactive (insert "?"))))))
;;#\DEVANAGARI_LETTER_SA used in infix for tensors involving symbolic expressions.
(addlispslimehook #'(lambda () (localsetkey (kbd "Cc s") (lambda () (interactive (insert "?"))))))
 Author
 See AUTHORS
 License
 LLGPL