Skip to content

Latest commit

 

History

History
389 lines (309 loc) · 13.3 KB

index.org

File metadata and controls

389 lines (309 loc) · 13.3 KB

thi.ng/ndarray

Contents

About the project

Description

This library is a Clojure/Clojurescript port of Mikola Lysenko’s ndarray core JS library with extended features to make more sense in a Clojure context.

Example usage

(require '[thi.ng.ndarray.core :as nd])

Transpose matrix

The ndarray constructor automatically coerces a Clojure seq into the correct array type (if not given as array already). transpose allows swapping the axis order without any copying.

(def a (nd/ndarray :float64 (range 16) [4 4]))
(seq a)
;; => (0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0)
(seq (nd/transpose a 1 0))
;; => (0.0 4.0 8.0 12.0 1.0 5.0 9.0 13.0 2.0 6.0 10.0 14.0 3.0 7.0 11.0 15.0)

Reverse direction in any dimension

Likewise, step order can be independently flipped/manipulated in any dimension…

(def a (nd/ndarray :float64 (range 16) [4 4]))
(seq (nd/step a -1 nil))
;; => (12.0 13.0 14.0 15.0 8.0 9.0 10.0 11.0 4.0 5.0 6.0 7.0 0.0 1.0 2.0 3.0)
(seq (nd/step a nil -1))
;; => (3.0 2.0 1.0 0.0 7.0 6.0 5.0 4.0 11.0 10.0 9.0 8.0 15.0 14.0 13.0 12.0)

Skip items in any dimension

(def a (nd/ndarray :float64 (range 16) [4 4]))
(seq (nd/step a 2 2))
;; => (0.0 2.0 8.0 10.0)

;; ...optionally with offset
(-> a (nd/truncate-l 0 1) (nd/step 2 2) seq)
;; => (1.0 3.0 9.0 11.0)

Extract slices of a volume

(def a (nd/ndarray :float32 (range 27) [3 3 3]))
(seq (nd/pick a 2 nil nil))
;; => (18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0)
(seq (nd/pick a nil 2 nil))
;; => (6.0 7.0 8.0 15.0 16.0 17.0 24.0 25.0 26.0)
(seq (nd/pick a nil nil 2))
;; => (2.0 5.0 8.0 11.0 14.0 17.0 20.0 23.0 26.0)
(seq (nd/pick a nil 0 0))
;; (0.0 9.0 18.0)

Fill subsection of an array

(def a (nd/ndarray :int8 (byte-array 25) [5 5]))
(dorun
 (for [i (-> a (nd/truncate-h 4 4) (nd/truncate-l 1 1) nd/index-seq)]
   (nd/set-at-index a i 1)))
(seq a)

;; (0 0 0 0 0
;;  0 1 1 1 0
;;  0 1 1 1 0
;;  0 1 1 1 0
;;  0 0 0 0 0)

Contour extraction

../assets/circle-res64.jpg

Code for this example is in the contours namespace.

Undirected graph queries via adjacency matrix

A slightly larger example to demonstrate connected component queries on a graph:

(defn adjacency-mat
  [ids]
  (let [n (count ids)]
    (nd/ndarray :boolean (repeat (* n n) false) [n n])))

(defn add-edge
  [index mat [a b]]
  (-> mat
      (nd/set-at (index a) (index b) true)
      (nd/set-at (index b) (index a) true)))

(defn edge?
  [index mat [a b]]
  (nd/get-at mat (index a) (index b)))

(defn neighbors
  [index rev-index mat x]
  (sequence
   (comp
    (map-indexed (fn [i rel] (if rel (rev-index i))))
    (filter identity))
   (nd/pick mat (index x) nil)))

(defn all-connected
  ([index rev-index mat q]
   (all-connected index rev-index mat #{} #{} [q]))
  ([index rev-index mat acc seen q]
   (let [n    (->> q
                   (filter (complement seen))
                   (mapcat #(neighbors index rev-index graph %)))
         acc' (into acc n)]
     (if (= acc acc')
       acc
       (recur index rev-index mat acc' (into seen q) n)))))

(def index {:a 0 :b 1 :c 2 :d 3 :e 4 :f 5})
(def rev-index (reduce-kv #(assoc % %3 %2) {} index))

(def graph
  (reduce
   (partial add-edge index)
   (adjacency-mat index)
   [[:a :b] [:b :d] [:d :c] [:e :f]]))

(edge? index graph [:b :a]) ;; => true
(edge? index graph [:a :c]) ;; => false
(neighbors index rev-index graph :c) ;; => (:a :d)

(all-connected index rev-index graph :a) ;; => #{:c :b :d :a}
(all-connected index rev-index graph :f) ;; => #{:e :f}

Namespaces

Tests

Project definition

Injected properties

0.3.3

Dependencies

Runtime

[org.clojure/clojure "1.11.1"]
[org.clojure/clojurescript "1.11.4"]
[thi.ng/typedarrays "0.1.7"]
[thi.ng/math "0.3.1"]

Development

[com.cemerick/clojurescript.test "0.3.3"]
[lein-cljsbuild "1.1.8"]
[lein-auto "0.1.2"]
[criterium "0.4.6"]

Leiningen coordinates

[thi.ng/ndarray "0.3.3"]

Building this project

This project is written in a literate programming format and requires Emacs & Org-mode to generate usable source code. Assuming both tools are installed, the easiest way to generate a working project is via command line (make sure emacs is on your path or else edit its path in tangle.sh):

git clone https://github.com/thi.ng/ndarray.git
cd ndarray
./tangle.sh src/*.org test/*.org

Tangling is the process of extracting & combining source blocks from .org files into an actual working project/source tree. Once tangling is complete, you can cd into the generated project directory (babel) and then use lein as usual.

Testing

The project.clj file defines an alias to trigger a complete build & tests for both Clojure & Clojurescript. Since PhantomJS (the usual test runner for other thi.ng projects) doesn’t support typed arrays, the cleantest command will run the Clojurescript tests in your default browser and test results are displayed in the dev console.

cd babel
lein cleantest

;; or use auto test runner via lein-auto
lein auto test

Tangling this file will also generate a small HTML harness for the resulting JS file and will be placed in the main folder (babel/index.html), allowing for further experimentation in the browser.

Leiningen project file

(defproject thi.ng/ndarray "<<version>>"
  :description  "ndarray for Clojure/Clojurescript"
  :url          "<<project-url>>"
  :license      {:name "Apache Software License 2.0"
                 :url "http://www.apache.org/licenses/LICENSE-2.0"
                 :distribution :repo}
  :scm          {:name "git"
                 :url "[email protected]:thi-ng/ndarray.git"}

  :min-lein-vesion "2.4.0"

  :dependencies [<<dep-clj>>
                 <<dep-cljs>>
                 <<dep-arrays>>
                 <<dep-math>>]

  :plugins      [<<dep-autotest>>]

  :profiles     {:dev {:dependencies [<<dep-criterium>>]
                       :plugins [<<dep-cljsbuild>>
                                 <<dep-cljs-test>>]
                       :global-vars {*warn-on-reflection* true}
                       :jvm-opts ^:replace []
                       :aliases {"cleantest" ["do" "clean," "test," "cljsbuild" "test"]}}}

  :auto         {:default {:file-pattern #"\.(clj|cljs|cljc)$"}}

  :cljsbuild    {:builds [{:id "simple"
                           :source-paths ["src" "test"]
                           :compiler {:output-to "<<cljs-artefact-path>>"
                                      :optimizations :whitespace
                                      :pretty-print true}}]
                 :test-commands {"unit-tests" ["open" :runner "index.html"]}}

  :pom-addition [:developers [:developer
                              [:name "Karsten Schmidt"]
                              [:url "https://thi.ng"]
                              [:timezone "1"]]])

ClojureScript HTML harness

<!DOCTYPE html>
<html lang="en">
  <head>
    <title><<lein-coords>> test</title>
  </head>
  <body>
    <script type="text/javascript" src="<<cljs-artefact-path>>"></script>
  </body>
</html>

Accessing library version during runtime

The autogenerated namespace thi.ng.ndarray.version contains a single symbol version holding the version string defined above:

(use '[thi.ng.ndarray.version])

(prn version)
; "<<version>>"

Version namespace

Release history

VersionReleasedDescriptionLein coordinatesTagged Github URL
0.3.32022-04-23update deps[thi.ng/ndarray "0.3.3"]0.3.3
0.3.22016-03-19update thi.ng/math dep[thi.ng/ndarray "0.3.2"]0.3.2
0.3.12016-03-19fix refl. warning, refactor reduce impls, update deps[thi.ng/ndarray "0.3.1"]0.3.1
0.3.02015-06-20CollReduce support, cljs bugfix contour ns, update deps[thi.ng/ndarray "0.3.0"]0.3.0
0.2.02015-06-14add contour ns, bugfixes, updated deps[thi.ng/ndarray "0.2.0"]0.2.0
0.1.02015-05-31initial test release[thi.ng/ndarray "0.1.0"]0.1.0

Contributors

NameRoleWebsite
Karsten Schmidtinitiator & principal developerhttps://thi.ng

I’ve got a fairly detailed roadmap and task list to implement over the coming months, but am always happy to receive feedback & suggestions and have issues filed. Once the core engine is more refined I’ll be gladly welcoming other contributions. Thanks for understanding!

License

This project is open source and licensed under the Apache Software License 2.0.