vin
vin

Reputation: 1019

clojure.core.matrix:: Mutate element in matrix

I am using a matrix as follows

(require '[clojure.core.matrix :as ccm])
(def M (ccm/matrix [[1 2] [3 4]]))
(ccm/mset! M 0 0 10)

However this throws an error

IllegalArgumentException No implementation of method: :set-2d! of protocol: #'clojure.core.matrix.protocols/PIndexedSettingMutable found for class: clojure.lang.PersistentVector clojure.core/-cache-protocol-fn (core_deftype.clj:568)

However this should work according to the wiki https://mikera.github.io/core.matrix/doc/clojure.core.matrix.html#var-mset.21

Upvotes: 4

Views: 662

Answers (2)

Mars
Mars

Reputation: 8854

core.matrix can use different underlying matrix implementations. By default it uses the persistent-vector implementation, which means that matrices are simply Clojure vectors containing Clojure vectors. However, Clojure vectors are immutable, so mset! won't work on them. This is the expected behavior.

To use mset!, you need to use one of the core.matrix implementations that can make mutable matrices. ndarray is the matrix implementation that's always available by default in core.matrix, but you have to tell core.matrix that you want to use it.

user=> (use 'clojure.core.matrix)
nil
user=> (matrix [[1 2] [3 4]])
[[1 2] [3 4]]

That's what a persistent-vector matrix looks like.

You can create an ndarray matrix by passing an extra keyword to the matrix function:

user=> (def M (matrix :ndarray [[1 2] [3 4]]))
#'user/M
user=> M
#object[clojure.core.matrix.impl.ndarray_object.NDArray 0x561ddbde "[[1 2] [3 4]]"]

Now mset! will work:

user=> (mset! M 0 0 10)
10
user=> M
#object[clojure.core.matrix.impl.ndarray_object.NDArray 0x561ddbde "[[10 2] [3 4]]"]

Notice the elements over on the right. You can also use pm to create a nicer representation:

user=> (pm M)
[[10 2]
 [ 3 4]]

To make ndarray the default, use set-current-implementation:

user=> (set-current-implementation :ndarray)
:ndarray

user=> (def M (matrix [[1 2] [3 4]]))
#'user/M

user=> M
#object[clojure.core.matrix.impl.ndarray_object.NDArray 0x5cd44e57 "[[1 2] [3 4]]"]

user=> (mset! M 0 0 10)
10

user=> M
#object[clojure.core.matrix.impl.ndarray_object.NDArray 0x5cd44e57 "[[10 2] [3 4]]"]

There are several other implementations available if you set them up as dependencies (e.g. in your Leiningen project.clj). Some of them support both mutable and immutable matrices.

By the way, as amalloy suggested, you can get a mutable matrix from an immutable matrix by passing the immutable matrix to mutable. If you do this with a persistent-vector matrix, the returned matrix will not be a persistent-vector matrix, since there are no mutable persistent-vector matrices. The new matrix will be a matrix from a different implementation. Which implementation is used depends on your current environment, but if you haven't done anything special, an ndarray matrix should be what is returned.

Upvotes: 2

amalloy
amalloy

Reputation: 91887

However this should work according to the wiki

No, the link you provided clearly states that it will throw an exception if the matrix is not mutable. The documentation for matrix isn't clear about whether it produces a mutable matrix or not, but evidently it does not. However, it is not hard to find a way to produce a mutable matrix: the function mutable is right there on the same page.

Upvotes: 1

Related Questions