Operations on matrices are not directly supported by the Clojure language but are implemented through the core.matrix specification. Trying to add two matrices in the REPL, as shown in the following code snippet, simply throws an error stating that a vector was found where an integer was expected:
user> (+ (matrix [[0 1]]) (matrix [[0 1]])) ClassCastException clojure.lang.PersistentVector cannot be cast to java.lang.Number clojure.lang.Numbers.add (Numbers.java:126)
This is because the +
function operates on numbers rather than matrices. To add matrices, we should use functions from the core.matrix.operators
namespace. The namespace declaration should look like the following code snippet after we have included core.matrix.operators
:
(ns my-namespace (:use clojure.core.matrix) (:require [clojure.core.matrix.operators :as M]))
Note that the functions are actually imported into an aliased namespace, as function names such as +
and *
conflict with those in the default Clojure namespace. In practice, we should always try to use aliased namespaces via the :require
and :as
filters and avoid the :use
filter. Alternatively, we could simply not refer to conflicting function names by using the :refer-clojure
filter in the namespace declaration, which is shown in the following code. However, this should be used sparingly and only as a last resort.
For the code examples in this section, we will use the previous declaration for clarity:
(ns my-namespace (:use clojure.core.matrix) (:require clojure.core.matrix.operators) (:refer-clojure :exclude [+ - *]))
We can use the M/+
function to perform the matrix addition of two or more matrices. To check the equality of any number of matrices, we use the M/==
function:
user> (def A (matrix [[0 1 2] [3 4 5]])) #'user/A user> (def B (matrix [[0 0 0] [0 0 0]])) #'user/B user> (M/== B A) false user> (def C (M/+ A B)) #'user/C user> C [[0 1 2] [3 4 5]] user> (M/== C A) true
Two matrices A and B are said to be equal if the following equality holds true:
Hence, the preceding equation explains that two or more matrices are equal if and only if the following conditions are satisfied:
The following is a simple, yet elegant implementation of matrix equality. It's basically comparing vector equality using the standard reduce
and map
functions:
(defn mat-eq "Checks if two matrices are equal" [A B] (and (= (count A) (count B)) (reduce #(and %1 %2) (map = A B))))
We first compare the row lengths of the two matrices using the count
and =
functions, and then use the reduce
function to compare the inner vector elements. Essentially, the reduce
function repeatedly applies a function that accepts two arguments to consecutive elements in a sequence and returns the final result when all the elements in the sequence have been reduced by the applied function.
Alternatively, we could use a similar composition using the every?
and true?
Clojure functions. Using the expression (every? true? (map = A B))
, we can check the equality of two matrices. Keep in mind that the true?
function returns true
if it is passed true
(and false
otherwise), and the every?
function returns true
if a given predicate function returns true
for all the values in a given sequence.
To add two matrices, they must have an equal number of rows and columns, and the sum is essentially a matrix composed of the sum of elements with the same row and column indices. The sum of two matrices A and B has been formally defined as follows:
It's almost trivial to implement matrix addition using the standard mapv
function, which is simply a variant of the map
function that returns a vector. We apply mapv
to each row of the matrix as well as to the entire matrix. Note that this implementation is intended for a vector of vectors, although it can be easily used with the matrix
and as-vec
functions from core.matrix to operate on matrices. We can implement the following function to perform matrix addition using the standard mapv
function:
(defn mat-add "Add two matrices" [A B] (mapv #(mapv + %1 %2) A B))
We can just as easily generalize the mat-add
function for any number of matrices by using the reduce
function. As shown in the following code, we can extend the previous definition of mat-add
to apply it to any number of matrices using the reduce
function:
(defn mat-add "Add two or more matrices" ([A B] (mapv #(mapv + %1 %2) A B)) ([A B & more] (let [M (concat [A B] more)] (reduce mat-add M))))
An interesting unary operation on a matrix A is the trace of a matrix, represented as . The trace of a matrix is essentially the sum of its diagonal elements:
It's fairly simple enough to implement the trace function of a matrix using the cl/map-indexed
and repeatedly
functions as described earlier. We have skipped it here to serve as an exercise for you.