This post is inspired by this other post that does the same in Python.
What you'll find is an explanation in pure Clojure of common math symbols, none of the implementations can be considered efficient.
$$\Large{ x_i }$$
(def x [1 2 3])
(x 0)
The tech stack is a very large collection of libraries for dealing with everything numeric. Here I make direct use of the tech.datatype library.
(require '[[tech.v2.datatype :as dtype]
[tech.v2.tensor :as tens]
[tech.v2.datatype.functional :as dtype-fn]])
(def x-tech (tens/->tensor x))
;;#tech.v2.tensor<float64>[3]
;;[1,000 2,000 3,000]
(dtype/get-value x 0)
;;1.0
Neanderthal is another highly optimized numeric library.
(require '[[uncomplicate.neanderthal.core :as nc]
[uncomplicate.neanderthal.native :as nn]
[uncomplicate.neanderthal.vect-math :as nm]])
(def x-nn (nn/dv x))
;;#RealBlockVector[double, n:3, offset: 0, stride:1]
;;[ 1.00 2.00 3.00 ]
(nc/entry x-nn 0)
;;1.0
$$\Large{ x_{ij} }$$
(def x [[1 2 3] [4 5 6]])
((x 0) 1)
;or
(get-in x [0 1])
(def x-tech (tens/->tensor x))
;;#tech.v2.tensor<float64>[2 3]
;;[[1,000 2,000 3,000]
;;
;; [4,000 5,000 6,000]]
(tens/select x-tech [0] [1])
;;#tech.v2.tensor<float64>[1 1]
;;[[2,000]]
(def x-nn (nn/dge 2 3 x {:layout :row}))
;;#RealGEMatrix[double, mxn:2x3, layout:row, offset:0]
;; ▤ ↓ ↓ ↓ ┓
;; → 1.00 2.00 3.00
;; → 4.00 5.00 6.00
;; ┗ ┛
(nc/entry x-nn 0 1)
;;2.0
Sum of all elements in a vector for a given range.
$$\Large{ \sum_{i=1}^N x_i }$$
(reduce + (range 5))
(dtype-fn/sum (range 5))
;or
(dtype-fn/sum (tens/->tensor (range 5)))
(nc/sum (nn/dv (range 5)))
$$\Large{ \frac{1}{N} \sum_{i=1}^{N} x_i }$$
(def x [1 2 3 4 5])
(/ (reduce + x) (count x))
(def x-tech (tens/->tensor x))
(dtype-fn// (dtype-fn/sum x-tech) (dtype/shape x-tech))
;or
(dtype-fn/mean x-tech)
(def x-nn (nn/dv x))
(/ (nc/sum x-nn) (nc/dim x-nn))
$$\Large{ \prod_{i=1}^{N}x_i }$$
Product of all elements in a vector for a given range.
(reduce * (range 1 6))
(dtype-fn/product x-tech)
(reduce * x-nn)
Pipe can mean different things:
$$\Large{ |x| }$$
Take the absolute value of \(x\) (no sign).
(defn abs [x]
(if (neg? x)
(-' x)
x))
(abs -3)
(dtype-fn/abs -3)
(nm/abs (nn/dv [-3]))
;or
(uncomplicate.neanderthal.math/abs -3)
$$\Large{ |x| }$$
$$\Large{ ||x|| }$$
The norm calculates the magnitude of a vector: square each element of a vector, sum them and then take the square root.
(defn norm
[v]
(let [square #(Math/pow % 2)
sqrt #(Math/sqrt %)]
(->> (map square v) (reduce +) sqrt)))
(norm [1 2 3])
(defn tech-norm
[v]
(dtype-fn/sqrt (dtype-fn/sum-of-squares v)))
(tech-norm [1 2 3])
(nc/nrm2 (nn/dv [1 2 3]))
$$\Large{ 3 \in X }$$
Is \(3\) part of the set \(X\)?
(defn epsilon
[x v]
(boolean ((set v) x)))
(epsilon 3 [1 2 3])
(dtype-fn/binary-search (tens/->tensor [1 2 3]) 3)
(filter #(= 3.0 %) x-nn)
$$\Large{ f : X \mapsto Y }$$
This is ANY regular function in Clojure since every function always returns a value.
(defn f
[x]
(+ x 2))
$$\Large{ f : R \mapsto R }$$
This means that the function takes and outputs only real numbers.
$$\Large{ f : R^d \mapsto R }$$
Here \(f\) takes as input a \(d\) dimensional vector and outputs a real number. \(\sum\) is an example of this.
(defn sum
[v]
(reduce + v))
This is the factorial of a number.
$$\Large{ x! }$$
(defn factorial [n]
(reduce * (range 1 (inc n))))
(factorial 5)
$$\Large{ X^T }$$
Exchange rows and columns of a matrix.
(defn transpose [m]
(apply mapv vector m))
(transpose [[1 2 3] [4 5 6]])
(tens/transpose [[1 2 3] [4 5 6]] [1 0])
(nc/trans (nn/dge 2 3 (range 1 7) {:layout :row}))
$$\Large{ x \odot y }$$
Multiply the corresponding elements in two vectors.
(def x [[1 2] [3 4]])
(def y [[2 2] [2 2]])
(map #(apply map * %)
(map vector x y))
(dtype-fn/* (tens/->tensor x) (tens/->tensor y))
(nm/mul (nn/dge 2 2 x {:layout :row}) (nn/dge 2 2 y {:layout :row}))
Sum of the products of the corresponding entries of 2 vectors.
$$\Large{ XY }$$
$$\Large{ X \cdot Y }$$
(defn dot
[& vs]
(reduce + (apply map * vs)))
(dot [1 2 3] [4 5 6])
(dtype-fn/dot-product [1 2 3] [4 5 6])
(nc/dot (nn/dv [1 2 3]) (nn/dv [4 5 6]))
$$\Large{ \hat{x} }$$
This gives the unit vector, and we get it by dividing each element by the norm.
(def x [1 2 3])
(map #(/ % (norm x)) x)
(dtype-fn// x (tech-norm x))
(def x-nn (nn/dv [1 2 3]))
(nc/ax (/ 1 (nc/nrm2 x-nn)) x-nn)