-
Notifications
You must be signed in to change notification settings - Fork 113
Getting Started Guide
This Wiki page provides a quick introduction to get started with core.matrix, and an overview of key features.
First task is to ensure that core.matrix is available in your project. The simplest way is to include it as a dependency from Clojars using either Leiningen or Maven. The latest dependency information is:
Note that you will need Clojure 1.7.0
as a minimum as of core.matrix release 0.5.0
. This is because core.matrix now uses .cljc
compilation to support both Clojure and ClojureScript.
The majority for the core.matrix API is provided as functions in the clojure.core.matrix
namespace. You can either use
or require
this namespace. If requiring, the usual convention is to use the alias m
so that functions can be accesses as m/add
etc.
For the purposes of this guide we will use
the namespace at the REPL as follows:
(use 'clojure.core.matrix)
Once we have core.matrix loaded, it is simple to start manipulating data using core.matrix functions. Arguments to core.matrix API functions are typically called arrays, which refers to any value that can be considered as a multi-dimensional array (see also : The Array Abstraction). Examples of arrays include:
-
[1 2 3]
- a one-dimensional vector of three numbers, represented by a Clojure vector -
[[1 2] [3 4]]
- a two-dimensional 2x2 matrix, represented by a Clojure vector of vectors -
[[[[1]]]]
- a four-dimensional 1x1x1x1 array, represented by nested Clojure vectors
Arrays all have a shape, which is defined as the vector of sizes of each dimension. Some examples:
(shape [[1 2] [3 4]]) ;; a 2x2 matrix
;; => [2 2]
(shape [[1 2 3] [4 5 6]]) ;; a 2x3 matrix
;; => [2 3]
(shape [1 2 3 4 5]) ;; a length 5 vector
;; => [5]
(shape 8.0) ;; a numerical scalar value
;; => nil ;; not an array, so doesn't have any shape
Examples of other useful functions that act on arrays include:
(ecount [[1 2 3] [4 5 6]]) ;; element count of an array
;; => 6
(esum [1 2 3 4 5]) ;; sum of elements in an array
;; => 15
(transpose [[1 2] [3 4]]) ;; transpose an array
;; => [[1 3] [2 4]]
core.matrix provides a wide variety of array operations, but also works hard to make array programming idiomatic for Clojure users. As part of this, a number of "functional programming" style operations are provided as part of the core.matrix API. Examples:
(emap inc [1 2 3 4]) ;; elementwise map - apply 'inc' to all elements
;; => [2 3 4 5]
(ereduce * [[1 2] [3 4]]) ;; elementwise reduce: reduce over all elements
;; => 24
Clojure vectors are versatile but may not be the best choice in all circumstances - you can get much better performance from specialised core.matrix implementations.
A good alternative for fast vector / matrix maths when you still want to stick to reliable pure-JVM code is the vectorz-clj implementation. You can add it as a dependency to your project as follows:
With the dependency added, it is possible to swith to the :vectorz
implementation with a single command:
(set-current-implementation :vectorz)
After switching the current implementation, core.matrix API functions will default to using :vectorz
arrays. You can see this as follows:
(def v (array [1 2 3]))
v
;; => #vectorz/vector [1.0,2.0,3.0]
(class v)
;; => mikera.vectorz.Vector
(def m (array [[1 -1 2] [-1 0 3]]))
m
;; #vectorz/matrix [[1.0,-1.0,2.0],[-1.0,0.0,3.0]]
(class m)
;; => mikera.matrixx.Matrix
(mmul m v)
;; => #vectorz/vector [5.0,8.0]
There are a few interesting things to note here:
- Although we provided
Long
numeric values to thearray
function, Vectorz has converted these to double values. This is a feature of Vectorz - it always performs double-precision calculations so numerical values are converted during array construction. This enables greater efficiency by consistent use ofdouble
primitives within the library. - The
array
function now produces:vectorz
objects by default, in this case instances of the classmikera.vectorz.Vector
andmikera.matrixx.Matrix
. Note that the implementation is "smart" and returns the right class for the shape of array requested. - The
:vectorz
objects are printed as tagged Clojure literals. This is a feature ofvectorz-clj
to make it easier to print and read array objects - The
mmul
function works normally on:vectorz
objects. It returns a vector result as expected, that is also a:vectorz
object.
Note that is is usually possible to mix parameters across different implementations. The behaviour and resulting is usually determined by the implementation of the first argument to the API function, e.g.
(mmul [[1 -1 2] [-1 0 3]] v) ;; First argument is a Clojure vector
;; => [5.0 8.0] ;; Result is a Clojure vector!
(mmul m [1 2 3]) ;; First argument is a :vectorz vector
;; => #vectorz/vector [5.0,8.0] ;; Result is a :vectorz vector!
For information on other implementations that may be available see Matrix Implementations.