Skip to content

Getting Started Guide

Ravindra R. Jaju edited this page Sep 27, 2017 · 18 revisions

This Wiki page provides a quick introduction to get started with core.matrix, and an overview of key features.

1. Adding the core.matrix dependency

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:

Clojars Project

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.

2. Use or require the clojure.core.matrix API namespace

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)

3. Using API functions with Clojure vectors

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]]

4. "Functional" operations on arrays

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

5. Using the :vectorz implementation

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:

Clojars Project

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 the array 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 of double primitives within the library.
  • The array function now produces :vectorz objects by default, in this case instances of the class mikera.vectorz.Vector and mikera.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 of vectorz-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.