An obvious stuff:
> (identity-matrix :ndarray 3)
#<NDArray [[1.0 0.0 0.0] [0.0 1.0 0.0] [0.0 0.0 1.0]]>
> (identity-matrix :ndarray-long 3)
#<NDArrayLong [[1 0 0] [0 1 0] [0 0 1]]>
> (identity-matrix :ndarray-double 3)
#<NDArrayDouble [[1.0 0.0 0.0] [0.0 1.0 0.0] [0.0 0.0 1.0]]>
What about matrix operations? Easy:
> (mul (diagonal-matrix :ndarray [1 2 3]) [1 2 3])
#<NDArray [1.0 4.0 9.0]>
> (add (diagonal-matrix :ndarray [1 2 3]) [1 2 3])
#<NDArray [[2 2.0 3.0] [1.0 4 3.0] [1.0 2.0 6]]>
Second example shows that number types can be ambigous with plain object-based NDArrays. Let's use primitive arrays instead:
> (add (diagonal-matrix :ndarray-long [1 2 3]) [1 2 3])
#<NDArrayLong [[2 2 3] [1 4 3] [1 2 6]]>
> (add (diagonal-matrix :ndarray-double [1 2 3]) [1 2 3])
#<NDArrayDouble [[2.0 2.0 3.0] [1.0 4.0 3.0] [1.0 2.0 6.0]]>
No ambiguity left! However, plain NDArray has it's advantages:
> (def mtx (diagonal-matrix :ndarray [#{1 2} #{2 3} #{3 4}]))
> mtx
#<NDArray [[#{1 2} 0.0 0.0] [0.0 #{2 3} 0.0] [0.0 0.0 #{3 4}]]>
> (emap number? mtx)
#<NDArray [[false true true] [true false true] [true true false]]>
Because it's just objects array, you can use anything as a matrix element. Just be cautious about math operations on such arrays.
What abount higher dimensions? They work, too:
> (def a (array :ndarray [[[1 2] [3 4]] [[5 6] [7 8]]]))
> a
#<NDArray [[[1 2] [3 4]] [[5 6] [7 8]]]>
> (slice a 0)
[[1 2] [3 4]]
> (slice a 1)
[[5 6] [7 8]]
> (-> a (slice 0) (slice 0))
[1 2]
> (-> a (slice 0) (slice 0) (slice 0))
1
> (reshape a [2 4])
#<NDArray [[1 2 3 4] [5 6 7 8]]>