diff --git a/src/tools/calculus.cljc b/src/tools/calculus.cljc new file mode 100644 index 0000000..b090caf --- /dev/null +++ b/src/tools/calculus.cljc @@ -0,0 +1,33 @@ +(ns tools.calculus) + +(defonce ^:const dx 0.0001) + +(defn deriv + "Returns the derivative of f evaluated at c. If called with only one argument, + it returns the derivative function." + ([f c] + ((deriv f) c)) + ([f] + (fn [x] + (/ (- (f (+ x dx)) (f x)) dx)))) + +(defn integrate + "Returns the definite integral of f over [a, b] using Simpson's method. + If called with only one argument (the function), returns the indefinite + integral, which takes as input a value x and (optionally) a constant c." + ([f] + (fn this + ([x] (this x 0)) + ([x c] (+ (integrate f 0 x) c)))) + ([f a b] + (let [n (/ 1 dx) + h (/ (- b a) n)] + (loop [i 1 + sum1 (f (+ a (/ h 2))) + sum2 0] + (if (< i n) + (recur (inc i) + (+ sum1 (f (+ a (* h i) (/ h 2)))) + (+ sum2 (f (+ a (* h i))))) + (* (/ h 6) (+ (f a) (f b) (* 4 sum1) (* 2 sum2)))))))) + diff --git a/src/tools/distributions.cljc b/src/tools/distributions.cljc new file mode 100644 index 0000000..03fbe11 --- /dev/null +++ b/src/tools/distributions.cljc @@ -0,0 +1,59 @@ +(ns tools.distributions + (:require [tools.math :as math]) + (:require [tools.calculus :as calculus])) + +;; ----------------------------------------------------------------------------- +;; NORMAL +;; ----------------------------------------------------------------------------- + +(defn- box-muller + "Given two uniformly distributed random variables (from 0 to 1), returns a + Standard Normal variable computed using the Box-Muller Transform." + [u1 u2] + (* (math/sqrt (* -2 (math/log u1))) + (math/cos (* 2 math/PI u2)))) + +(defn- normal-pdf + "Given a mean and standard deviation, returns the corresponding Normal + Probability Distribution Function." + [mu sigma] + (fn [x] + (* (/ 1 (* sigma (math/sqrt (* 2 math/PI)))) + (math/exp (- (/ (math/pow (/ (- x mu) sigma) 2) 2)))))) + +(defn rand-norm + "Generates n Normally-distributed random variables with given mean and + standard deviation. If no parameters are provided, defaults to a + single random observation from a Standard Normal distribution. + Accepts an argument map with optional keys :n, :mu, and :sigma." + [{:keys [n mu sigma] + :or {n 1, mu 0, sigma 1}}] + (repeatedly n #(box-muller (rand 1) (rand 1)))) + +(defn pdf-norm + "Returns the value of the Normal Probability Distribution Function at a + particular value x. If no distributional parameters are provided, defaults to + the Standard Normal PDF. + Accepts an argument map with keys :x, and optionally :mu and :sigma." + [{:keys [x mu sigma] + :or {mu 0, sigma 1}}] + ((normal-pdf mu sigma) x)) + +(defn cdf-norm + "Parameters: {:keys [x mu sigma]} + Returns the value of the Normal Cumulative Distribution Function at a + particular value x. If no distributional parameters are provided, defaults to + the Standard Normal CDF. + Accepts an argument map with keys :x, and optionally :mu and :sigma." + [{:keys [x mu sigma] + :or {mu 0, sigma 1}}] + (calculus/integrate (normal-pdf mu sigma) (- mu (* 6 sigma)) x)) + +(defn quant-norm + "For a given probability p, returns the corresponding value of the quantile + function (i.e. the inverse Cumulative Distribution Function). If no + distributional parameters are provided, defaults to Standard Normal quantiles. + Accepts an argument map with keys :p, and optionally :mu and :sigma." + [{:keys [p mu sigma] + :or {mu 0, sigma 1}}] + ()) ; unfinished... diff --git a/src/tools/math.cljc b/src/tools/math.cljc new file mode 100644 index 0000000..91a2ae5 --- /dev/null +++ b/src/tools/math.cljc @@ -0,0 +1,99 @@ +(ns tools.math) + +(defonce PI #?(:clj Math/PI + :cljs js/Math.PI)) + +(defonce E #?(:clj Math/E + :cljs js/Math.PI)) + +(defn abs + "Returns the absolute value of a number." + [x] + (if (neg? x) (- x) x)) + +(defn approx= [x y epsilon] + "Returns true if the absolute difference between x and y is less than or + equal to some specified error level, epsilon." + (<= (abs (- y x)) epsilon)) + +(defn ceil + "Returns the smallest integer greater than or equal to x." + [x] + #?(:clj (Math/ceil x) + :cljs (js/Math.ceil x))) + +(defn cos + "Returns the cosine of an angle (specified in radians)." + [x] + #?(:clj (Math/cos x) + :cljs (js/Math.cos x))) + +(defn exp + "Returns Euler's number (approx. 2.71) raised to the given power." + [x] + #?(:clj (Math/exp x) + :cljs (js/Math.exp x))) + +(defn floor + "Returns the largest integer less than or equal to x." + [x] + #?(:clj (Math/floor x) + :cljs (js/Math.floor x))) + +(defn log + "Returns the logarithm of x with the given base. If called with only one + argument, returns the natural logarithm (base e) of the given value." + ([x base] + (/ (log x) (log base))) + ([x] + #?(:clj (Math/log x) + :cljs (js/Math.log x)))) + +(defn pow + "Returns the value obtained by raising the first argument to the power of + the second argument." + [x n] + #?(:clj (Math/pow x n) + :cljs (js/Math.pow x n))) + +(defn root + "Returns the root of x with base n." + [x n] + (pow x (/ 1 n))) + +(defn round + "Returns the value of x rounded to the nearest integer." + [x] + #?(:clj (Math/round x) + :cljs (js/Math.round x))) + +(defn sign + "Returns the 1 if the argument is positive, -1 if the argument is negative, + and 0 if the argument is zero." + [x] + (cond (< x 0) -1 + (> x 0) 1 + :else 0)) + +(defn sin + "Returns the sine of an angle (specified in radians)." + [x] + #?(:clj (Math/sin x) + :cljs (js/Math.sin x))) + +(defn sqrt + "Returns the square root of the given value." + [x] + #?(:clj (Math/sqrt x) + :cljs (js/Math.sqrt x))) + +(defn square + "Returns the square of the given value." + [x] + (* x x)) + +(defn tan + "Returns the tangent of an angle (specified in radians)." + [x] + #?(:clj (Math/tan x) + :cljs (js/Math.tan x)))