The Kitchen Sink and Other Oddities

Atabey Kaygun

Nonnegative Matrix Decomposition in Clojure

Description of the problem

I did a scala implementation of non-negative matrix decomposition yesterday. Today, I am going to implement it in clojure.

An implementation in Clojure

For this post (as I did in my other posts in Clojure) I use boot. We declare our dependencies:

(set-env! :dependencies 
          '[[net.mikera/vectorz-clj "0.48.0"]
            [net.mikera/core.matrix "0.62.0"]])

(ns nonnegative.core
    (:gen-class)
    (:require [clojure.core.matrix :as cm]))

core.matrix has several backends to perform matrix calculations. Today, we are going to use vectorz.

(clojure.core.matrix.implementations/set-current-implementation :vectorz)

We are going to need two utility functions:

(defn random-matrix [n m]
  (as-> (repeatedly rand) $
    (take (* n m) $)
    (cm/reshape $ [n m])))

(defn cost-fn [A B]
  (->> (cm/sub A B) 
       (cm/to-vector)
       (map (fn [x] (* x x)))
       (reduce +)))

and here is our implementation:

(defn nnmd [D k epocs tol rate flag]
  (let [n (cm/row-count D)
        m (cm/column-count D)
        s (* n m)]
    (loop [W (random-matrix n k)
           H (random-matrix k m)
           i epocs
           c tol]
      (if (or (= i 0) (< c tol)) 
        [W H i c]
        (let [u (cm/reshape (take s (repeat 1)) [n m])
              Wt (cm/transpose W)
              Ht (cm/transpose H)
              et (cm/mul rate (cm/div W (cm/mmul u Ht)))
              mu (cm/mul rate (cm/div H (cm/mmul Wt u)))
              temp (cm/sub (cm/div D (cm/mmul W H)) u)]
          (if (and flag (= 0 (mod i 100))) (println i c))
          (recur (cm/add W (cm/mul et (cm/mmul temp Ht)))
                 (cm/add H (cm/mul mu (cm/mmul Wt temp)))
                 (dec i)
                 (/ (cost-fn D (cm/mmul W H)) s)))))))

Let us test:

(let [D (cm/scale 1e-4 (random-matrix 300 150))
     [W H i c] (nnmd D 50 3500 1e-2 1e-2 true)]
  (println i c))

3500 0.01
3400 2.967546510137647    
3300 0.05326918311432456
3258 0.009844411644088693